diff --git a/.idea/libraries/Netty.xml b/.idea/libraries/Netty.xml index ef0ce7ca25db..c5b98b3964c1 100644 --- a/.idea/libraries/Netty.xml +++ b/.idea/libraries/Netty.xml @@ -1,11 +1,11 @@ - + - + \ No newline at end of file diff --git a/images/src/org/intellij/images/util/ImageInfoReader.java b/images/src/org/intellij/images/util/ImageInfoReader.java index 1c809bbba28f..7dd764faa328 100644 --- a/images/src/org/intellij/images/util/ImageInfoReader.java +++ b/images/src/org/intellij/images/util/ImageInfoReader.java @@ -16,6 +16,7 @@ package org.intellij.images.util; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.io.UnsyncByteArrayInputStream; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -65,7 +66,7 @@ public class ImageInfoReader { @Nullable private static Info read(@NotNull final byte[] data) { - final DataInputStream is = new DataInputStream(new ByteArrayInputStream(data)); + final DataInputStream is = new DataInputStream(new UnsyncByteArrayInputStream(data)); try { return readFileData(is); } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index d836d5776302..38a4c9a8c716 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -966,7 +966,8 @@ public class CompileDriver { boolean didSomething = false; final CompilerManager compilerManager = CompilerManager.getInstance(myProject); - GenericCompilerRunner runner = new GenericCompilerRunner(context, myCompilerFilter, compilerManager, isRebuild, onlyCheckStatus); + GenericCompilerRunner runner = new GenericCompilerRunner(context, isRebuild, onlyCheckStatus, + compilerManager.getCompilers(GenericCompiler.class, myCompilerFilter)); try { didSomething |= generateSources(compilerManager, context, forceCompile, onlyCheckStatus); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/GenericCompilerRunner.java b/java/compiler/impl/src/com/intellij/compiler/impl/GenericCompilerRunner.java index e6e5cc500daa..f03a28617323 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/GenericCompilerRunner.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/GenericCompilerRunner.java @@ -53,14 +53,12 @@ public class GenericCompilerRunner { private final Project myProject; public GenericCompilerRunner(CompileContext context, - CompilerFilter compilerFilter, - CompilerManager compilerManager, boolean forceCompile, - boolean onlyCheckStatus) { + boolean onlyCheckStatus, final GenericCompiler[] compilers) { myContext = context; myForceCompile = forceCompile; myOnlyCheckStatus = onlyCheckStatus; - myCompilers = compilerManager.getCompilers(GenericCompiler.class, compilerFilter); + myCompilers = compilers; myProject = myContext.getProject(); } @@ -69,7 +67,7 @@ public class GenericCompilerRunner { try { for (GenericCompiler compiler : myCompilers) { if (compiler.getOrderPlace().equals(place)) { - didSomething = invokeCompiler(compiler); + didSomething |= invokeCompiler(compiler); } } } diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/SingleTargetCompilerInstance.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/SingleTargetCompilerInstance.java deleted file mode 100644 index f2e4dd3ca71e..000000000000 --- a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/SingleTargetCompilerInstance.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2000-2010 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.compiler.generic; - -import com.intellij.openapi.compiler.CompileContext; -import org.jetbrains.annotations.NotNull; - -import java.util.Collections; -import java.util.List; - -/** - * @author nik - */ -public abstract class SingleTargetCompilerInstance, K,S, O> extends - GenericCompilerInstance { - protected SingleTargetCompilerInstance(CompileContext context) { - super(context); - } - - @NotNull - @Override - public List getAllTargets() { - return Collections.singletonList(BuildTarget.DEFAULT); - } - - @NotNull - @Override - public List getSelectedTargets() { - return getAllTargets(); - } - - @Override - public void processObsoleteTarget(@NotNull String targetId, @NotNull List> obsoleteItems) { - } -} diff --git a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileCompileItem.java b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileCompileItem.java index 03a14bd0d63c..b1e5394fc60d 100644 --- a/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileCompileItem.java +++ b/java/compiler/impl/src/com/intellij/openapi/compiler/generic/VirtualFileCompileItem.java @@ -16,8 +16,6 @@ package com.intellij.openapi.compiler.generic; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.EnumeratorStringDescriptor; -import com.intellij.util.io.KeyDescriptor; import org.jetbrains.annotations.NotNull; /** diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java b/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java index 3b0e586020f2..ee2315e3a046 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/run/BuildArtifactsBeforeRunTaskProvider.java @@ -99,6 +99,7 @@ public class BuildArtifactsBeforeRunTaskProvider extends BeforeRunTaskProvider" : psiMethod.getName(); - myTargetMethodSignature = JVMNameUtil.getJVMSignature(psiMethod); + this(JVMNameUtil.getJVMQualifiedName(psiMethod.getContainingClass()), + psiMethod.isConstructor()? "" : psiMethod.getName(), + JVMNameUtil.getJVMSignature(psiMethod)); + } + + public SmartStepFilter(@NotNull JVMName declaringClassName, @NonNls String targetMethodName, + @NotNull JVMName targetMethodSignature) { + myDeclaringClassName = declaringClassName; + myTargetMethodName = targetMethodName; + myTargetMethodSignature = targetMethodSignature; } public String getTargetMethodName() { diff --git a/java/execution/openapi/src/com/intellij/execution/JavaExecutionUtil.java b/java/execution/openapi/src/com/intellij/execution/JavaExecutionUtil.java index 5097256ac559..89cc65ebeeaf 100644 --- a/java/execution/openapi/src/com/intellij/execution/JavaExecutionUtil.java +++ b/java/execution/openapi/src/com/intellij/execution/JavaExecutionUtil.java @@ -30,6 +30,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.ClassUtil; import com.intellij.psi.util.PsiClassUtil; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; @@ -106,14 +107,7 @@ public class JavaExecutionUtil { @Nullable public static String getRuntimeQualifiedName(final PsiClass aClass) { - final PsiClass containingClass = aClass.getContainingClass(); - if (containingClass != null) { - final String parentName = getRuntimeQualifiedName(containingClass); - return parentName + "$" + aClass.getName(); - } - else { - return aClass.getQualifiedName(); - } + return ClassUtil.getJVMClassName(aClass); } @Nullable 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 0c0abdce08d1..46812a81f005 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 @@ -16,7 +16,6 @@ package com.intellij.psi.impl.file; import com.intellij.ide.projectView.ProjectView; -import com.intellij.ide.projectView.impl.ProjectViewPane; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.undo.GlobalUndoableAction; import com.intellij.openapi.command.undo.UndoManager; @@ -139,7 +138,7 @@ public class PsiPackageImplementationHelperImpl extends PsiPackageImplementation final ProjectView projectView = ProjectView.getInstance(project); PsiDirectory[] directories = suggestMostAppropriateDirectories(psiPackage); if (directories.length == 0) return; - projectView.getProjectViewPaneById(ProjectViewPane.ID).select(directories[0], directories[0].getVirtualFile(), requestFocus); + projectView.select(directories[0], directories[0].getVirtualFile(), requestFocus); } }); } diff --git a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.form b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.form index b0ee1e42eb87..96720321c9fe 100644 --- a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.form +++ b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.form @@ -71,6 +71,7 @@ + @@ -81,11 +82,12 @@ + - + diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java index 22e98032eb8a..c9f49dbf0d19 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -16,15 +16,15 @@ package com.intellij.codeInsight; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.NullableComputable; import com.intellij.psi.*; import com.intellij.psi.controlFlow.*; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.InheritanceUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.util.*; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; @@ -347,18 +347,26 @@ public class ExceptionUtil { @NotNull public static List getUnhandledCloserExceptions(final PsiResourceVariable resource, @Nullable final PsiElement topElement) { - final PsiType resourceType = resource.getType(); - if (resourceType instanceof PsiClassType) { - final PsiClass resourceClass = ((PsiClassType)resourceType).resolve(); - if (resourceClass != null) { - final PsiMethod[] closers = resourceClass.findMethodsByName("close", false); - for (final PsiMethod method : closers) { - if (method.getParameterList().getParametersCount() == 0) { - return getUnhandledExceptions(method, resource, topElement, PsiSubstitutor.EMPTY); + final Project project = resource.getProject(); + final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + final PsiClass autoCloseable = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, ProjectScope.getLibrariesScope(project)); + if (autoCloseable != null) { + final PsiMethod[] methods = autoCloseable.findMethodsByName("close", false); + if (methods.length == 1) { + final MethodSignature signature = methods[0].getSignature(PsiSubstitutor.EMPTY); + final PsiType resourceType = resource.getType(); + if (resourceType instanceof PsiClassType) { + final PsiClass resourceClass = ((PsiClassType)resourceType).resolve(); + if (resourceClass != null) { + final PsiMethod method = MethodSignatureUtil.findMethodBySignature(resourceClass, signature, true); + if (method != null) { + return getUnhandledExceptions(method, resource, topElement, PsiSubstitutor.EMPTY); + } } } } } + return Collections.emptyList(); } diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index ccb8e83f70e2..510cab79e4de 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -27,6 +27,7 @@ import com.intellij.psi.tree.TokenSet; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.PropertyKey; import static com.intellij.lang.PsiBuilderUtil.expect; import static com.intellij.lang.PsiBuilderUtil.nextTokenType; @@ -86,7 +87,7 @@ public class DeclarationParser { } parseClassBodyDeclarations(builderWrapper, isAnnotation); - expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); + expectOrError(builder, JavaTokenType.RBRACE, "expected.rbrace"); } @Nullable @@ -139,7 +140,7 @@ public class DeclarationParser { } if (declarationsAfterEnd) { - expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); + expectOrError(builder, JavaTokenType.RBRACE, "expected.rbrace"); } } @@ -426,7 +427,7 @@ public class DeclarationParser { final boolean anno, final boolean constructor) { parseParameterList(builder); - eatBrackets(builder, constructor, JavaErrorMessages.message("expected.semicolon")); + eatBrackets(builder, constructor, "expected.semicolon"); if (areTypeAnnotationsSupported(builder)) { final PsiBuilder.Marker receiver = builder.mark(); @@ -489,8 +490,8 @@ public class DeclarationParser { builder.advanceLexer(); final IElementType delimiter = resources ? JavaTokenType.SEMICOLON : JavaTokenType.COMMA; - final String noDelimiterMsg = JavaErrorMessages.message(resources ? "expected.semicolon" : "expected.comma"); - final String noElementMsg = JavaErrorMessages.message(resources ? "expected.resource" : "expected.parameter"); + final String noDelimiterMsg = resources ? "expected.semicolon" : "expected.comma"; + final String noElementMsg = resources ? "expected.resource" : "expected.parameter"; PsiBuilder.Marker invalidElements = null; String errorMessage = null; @@ -548,7 +549,7 @@ public class DeclarationParser { if (invalidElements == null) { if (builder.getTokenType() == delimiter) { - error(builder, noElementMsg); + error(builder, JavaErrorMessages.message(noElementMsg)); builder.advanceLexer(); if (noElements && resources) { noElements = false; @@ -557,7 +558,7 @@ public class DeclarationParser { } else { invalidElements = builder.mark(); - errorMessage = delimiterExpected ? noDelimiterMsg : noElementMsg; + errorMessage = JavaErrorMessages.message(delimiterExpected ? noDelimiterMsg : noElementMsg); } } @@ -613,7 +614,7 @@ public class DeclarationParser { if (expect(builder, JavaTokenType.IDENTIFIER)) { if (!resource) { - eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, JavaErrorMessages.message("expected.rparen")); + eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, "expected.rparen"); done(param, JavaElementType.PARAMETER); return param; } @@ -624,7 +625,7 @@ public class DeclarationParser { return modListInfo.first; } - if (expectOrError(builder, JavaTokenType.EQ, JavaErrorMessages.message("expected.eq"))) { + if (expectOrError(builder, JavaTokenType.EQ, "expected.eq")) { if (myExpressionParser.parse(builder) == null) { error(builder, JavaErrorMessages.message("expected.expression")); } @@ -720,7 +721,8 @@ public class DeclarationParser { return declaration; } - private static boolean eatBrackets(final PsiBuilder builder, final boolean isError, @Nullable final String error) { + private static boolean eatBrackets(final PsiBuilder builder, final boolean isError, + @Nullable @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String errorKey) { if (builder.getTokenType() != JavaTokenType.LBRACKET) return true; final PsiBuilder.Marker marker = isError ? builder.mark() : null; @@ -735,7 +737,7 @@ public class DeclarationParser { } if (marker != null) { - marker.error(error); + marker.error(errorKey != null ? JavaErrorMessages.message(errorKey):null); } return result; @@ -840,9 +842,9 @@ public class DeclarationParser { pair = builder.mark(); } - final boolean hasName = expectOrError(builder, JavaTokenType.IDENTIFIER, JavaErrorMessages.message("expected.identifier")); + final boolean hasName = expectOrError(builder, JavaTokenType.IDENTIFIER, "expected.identifier"); - expectOrError(builder, JavaTokenType.EQ, JavaErrorMessages.message("expected.eq")); + expectOrError(builder, JavaTokenType.EQ, "expected.eq"); parseAnnotationValue(builder); diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index 8424d8defa81..2699fad161ca 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -404,7 +404,7 @@ public class ExpressionParser { final PsiBuilder.Marker refExpr = expr.precede(); myReferenceParser.parseReferenceParameterList(builder, false, false); - if (!JavaParserUtil.expectOrError(builder, JavaTokenType.IDENTIFIER, JavaErrorMessages.message("expected.identifier"))) { + if (!JavaParserUtil.expectOrError(builder, JavaTokenType.IDENTIFIER, "expected.identifier")) { refExpr.done(JavaElementType.REFERENCE_EXPRESSION); startMarker.drop(); return refExpr; @@ -726,7 +726,7 @@ public class ExpressionParser { } bracketCount++; - if (!JavaParserUtil.expectOrError(builder, JavaTokenType.RBRACKET, JavaErrorMessages.message("expected.rbracket"))) { + if (!JavaParserUtil.expectOrError(builder, JavaTokenType.RBRACKET, "expected.rbracket")) { newExpr.done(JavaElementType.NEW_EXPRESSION); return newExpr; } @@ -812,7 +812,7 @@ public class ExpressionParser { } } - final boolean closed = JavaParserUtil.expectOrError(builder, JavaTokenType.RPARENTH, JavaErrorMessages.message("expected.rparen")); + final boolean closed = JavaParserUtil.expectOrError(builder, JavaTokenType.RPARENTH, "expected.rparen"); list.done(JavaElementType.EXPRESSION_LIST); if (!closed) { @@ -827,35 +827,29 @@ public class ExpressionParser { @Nullable private static IElementType getGtTokenType(final PsiBuilder builder) { - final PsiBuilder.Marker sp = builder.mark(); - IElementType tokenType = builder.getTokenType(); - if (tokenType == JavaTokenType.GT) { - builder.advanceLexer(); - if (builder.getTokenType() == JavaTokenType.GT) { - builder.advanceLexer(); - if (builder.getTokenType() == JavaTokenType.GT) { - builder.advanceLexer(); - if (builder.getTokenType() == JavaTokenType.EQ) { - tokenType = JavaTokenType.GTGTGTEQ; - } - else { - tokenType = JavaTokenType.GTGTGT; - } - } - else if (builder.getTokenType() == JavaTokenType.EQ) { - tokenType = JavaTokenType.GTGTEQ; + if (tokenType != JavaTokenType.GT) return tokenType; + + if (builder.rawLookup(1) == JavaTokenType.GT) { + if (builder.rawLookup(2) == JavaTokenType.GT) { + if (builder.rawLookup(3) == JavaTokenType.EQ) { + tokenType = JavaTokenType.GTGTGTEQ; } else { - tokenType = JavaTokenType.GTGT; + tokenType = JavaTokenType.GTGTGT; } } - else if (builder.getTokenType() == JavaTokenType.EQ) { - tokenType = JavaTokenType.GE; + else if (builder.rawLookup(2) == JavaTokenType.EQ) { + tokenType = JavaTokenType.GTGTEQ; + } + else { + tokenType = JavaTokenType.GTGT; } } + else if (builder.rawLookup(1) == JavaTokenType.EQ) { + tokenType = JavaTokenType.GE; + } - sp.rollbackTo(); return tokenType; } diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java index d3fd09cc889f..6af4ce564138 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java @@ -39,6 +39,7 @@ import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.PropertyKey; import java.util.List; @@ -258,9 +259,10 @@ public class JavaParserUtil { } } - public static boolean expectOrError(final PsiBuilder builder, final IElementType expectedType, final String errorMessage) { + public static boolean expectOrError(final PsiBuilder builder, final IElementType expectedType, + @PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String errorMessageKey) { if (!PsiBuilderUtil.expect(builder, expectedType)) { - error(builder, errorMessage); + error(builder, JavaErrorMessages.message(errorMessageKey)); return false; } return true; @@ -275,7 +277,7 @@ public class JavaParserUtil { } public static void semicolon(final PsiBuilder builder) { - expectOrError(builder, JavaTokenType.SEMICOLON, JavaErrorMessages.message("expected.semicolon")); + expectOrError(builder, JavaTokenType.SEMICOLON, "expected.semicolon"); } public static PsiBuilder braceMatchingBuilder(final PsiBuilder builder) { diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/ReferenceParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/ReferenceParser.java index 68fd2ff197e3..9382af0cac6b 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/ReferenceParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/ReferenceParser.java @@ -293,7 +293,7 @@ public class ReferenceParser { if (expect(builder, JavaTokenType.GT)) { break; } - else if (!expectOrError(builder, JavaTokenType.COMMA, JavaErrorMessages.message("expected.gt.or.comma"))) { + else if (!expectOrError(builder, JavaTokenType.COMMA, "expected.gt.or.comma")) { isOk = false; break; } diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/StatementParser.java index b4294f6a4cd9..95ad7cfd72a8 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -131,7 +131,7 @@ public class StatementParser { parseStatements(builder, parseUntilEof ? BraceMode.TILL_LAST : BraceMode.TILL_FIRST); - final boolean greedyBlock = !expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); + final boolean greedyBlock = !expectOrError(builder, JavaTokenType.RBRACE, "expected.rbrace"); builder.getTokenType(); // eat spaces done(codeBlock, JavaElementType.CODE_BLOCK); @@ -543,7 +543,7 @@ public class StatementParser { } } - expectOrError(builder, JavaTokenType.COLON, JavaErrorMessages.message("expected.colon")); + expectOrError(builder, JavaTokenType.COLON, "expected.colon"); done(statement, JavaElementType.SWITCH_LABEL_STATEMENT); return statement; diff --git a/java/java-psi-impl/src/messages/JavaErrorMessages.properties b/java/java-psi-impl/src/messages/JavaErrorMessages.properties index 317aeb1326bb..1c83fe4b3480 100644 --- a/java/java-psi-impl/src/messages/JavaErrorMessages.properties +++ b/java/java-psi-impl/src/messages/JavaErrorMessages.properties @@ -328,6 +328,7 @@ expected.lparen.or.lbracket='(' or '[' expected expected.array.initializer=Array initializer expected unexpected.tokens=Unexpected tokens expected.gt.or.comma='>' or ',' expected. +string.expected=String literal expected else.without.if='else' without 'if' catch.without.try='catch' without 'try' finally.without.try='finally' without 'try' diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java index e8df782c83ac..c0f8eb7cc342 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java @@ -1,3 +1,18 @@ +/* + * 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. + */ class C { static class E extends Exception { } static class E1 extends E { } @@ -10,6 +25,8 @@ class C { @Override public void close() throws E3 { } } + static interface I extends AutoCloseable { } + void m1() { try (MyResource r = new MyResource()) { r.doSomething(); } catch (E1 | E2 | E3 ignore) { } @@ -24,6 +41,8 @@ class C { catch (E3 e) { } try (MyResource r = new MyResource()) { } + + try (I r = null) { System.out.println(r); } } void m2() throws Exception { diff --git a/java/java-tests/testData/psi/parser-partial/expressions/Binary2.txt b/java/java-tests/testData/psi/parser-partial/expressions/Binary2.txt new file mode 100644 index 000000000000..f9e60382ce09 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/Binary2.txt @@ -0,0 +1,18 @@ +PsiJavaFile:Binary2.java + PsiAssignmentExpression:a > = b + PsiBinaryExpression:a > + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:GT('>') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiReferenceExpression:b + PsiReferenceParameterList + + PsiIdentifier:b('b') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/Binary3.txt b/java/java-tests/testData/psi/parser-partial/expressions/Binary3.txt new file mode 100644 index 000000000000..5e5a0718daf8 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/Binary3.txt @@ -0,0 +1,18 @@ +PsiJavaFile:Binary3.java + PsiAssignmentExpression:a >/**/= b + PsiBinaryExpression:a > + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:GT('>') + PsiErrorElement:Expression expected + + PsiComment(C_STYLE_COMMENT)('/**/') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiReferenceExpression:b + PsiReferenceParameterList + + PsiIdentifier:b('b') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/lexer/JavaLexerTest.java b/java/java-tests/testSrc/com/intellij/lang/java/lexer/JavaLexerTest.java index 85e3069f767d..b4e835b2a079 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/lexer/JavaLexerTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/lexer/JavaLexerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -54,7 +54,7 @@ public class JavaLexerTest extends LexerTestCase { "DOUBLE_LITERAL ('1e137')"); } - public void testTigerNumericLiterals() throws Exception { + public void testTigerNumericLiterals() { doTest("0xap0f 0xab.p0F 0x.abcP0f 0xabc.defP0F", "FLOAT_LITERAL ('0xap0f')\nWHITE_SPACE (' ')\n" + "FLOAT_LITERAL ('0xab.p0F')\nWHITE_SPACE (' ')\n" + @@ -115,7 +115,7 @@ public class JavaLexerTest extends LexerTestCase { "DOUBLE_LITERAL ('0xa_bc.de_fP1_234D')"); } - public void testMalformedCoinLiterals() throws Exception { + public void testMalformedCoinLiterals() { doTest("0_ _1 0_8 0x_f 0b_1 0B2 0x1.0_p-1 1.0e_1022", "INTEGER_LITERAL ('0')\nIDENTIFIER ('_')\nWHITE_SPACE (' ')\n" + "IDENTIFIER ('_1')\nWHITE_SPACE (' ')\n" + @@ -127,6 +127,13 @@ public class JavaLexerTest extends LexerTestCase { "DOUBLE_LITERAL ('1.0e')\nIDENTIFIER ('_1022')"); } + public void testMalformedOperators() { + doTest("(i > = 0)", + "LPARENTH ('(')\nIDENTIFIER ('i')\nWHITE_SPACE (' ')\n" + + "GT ('>')\nWHITE_SPACE (' ')\nEQ ('=')\n" + + "WHITE_SPACE (' ')\nINTEGER_LITERAL ('0')\nRPARENTH (')')\n"); + } + @Override protected Lexer createLexer() { return new JavaLexer(LanguageLevel.HIGHEST); diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java index fdc02ee9bf87..471190ed3179 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -32,6 +32,8 @@ public class ExpressionParserTest extends JavaParsingTestCase { public void testBinary0() { doParserTest("a + b"); } public void testBinary1() { doParserTest("a < b"); } + public void testBinary2() { doParserTest("a > = b"); } + public void testBinary3() { doParserTest("a >/**/= b"); } public void testCond0() { doParserTest("cond ? true : false"); } public void testCond1() { doParserTest("cond ?"); } diff --git a/java/openapi/src/com/intellij/psi/util/ClassUtil.java b/java/openapi/src/com/intellij/psi/util/ClassUtil.java index 18038c40c13d..d7f8aa2a27c2 100644 --- a/java/openapi/src/com/intellij/psi/util/ClassUtil.java +++ b/java/openapi/src/com/intellij/psi/util/ClassUtil.java @@ -245,26 +245,19 @@ public class ClassUtil { @Nullable public static String getJVMClassName(PsiClass aClass) { - final String qName = aClass.getQualifiedName(); - if (qName == null) return null; - return replaceDotsWithDollars(qName, aClass); - } - - private static String replaceDotsWithDollars(final String qName, PsiClass aClass) { - StringBuilder qNameBuffer = new StringBuilder(qName); - - int fromIndex = qNameBuffer.length(); - PsiElement parent = aClass.getParent(); - while (parent instanceof PsiClass) { - final int dotIndex = qNameBuffer.lastIndexOf(".", fromIndex); - if (dotIndex < 0) break; - qNameBuffer.replace(dotIndex, dotIndex + 1, "$"); - fromIndex = dotIndex - 1; - parent = parent.getParent(); + final PsiClass containingClass = aClass.getContainingClass(); + if (containingClass != null) { + String parentName = getJVMClassName(containingClass); + if (parentName == null) { + return null; + } + + return parentName + "$" + aClass.getName(); } - return qNameBuffer.toString(); + return aClass.getQualifiedName(); } + @Nullable public static PsiClass findPsiClassByJVMName(final PsiManager manager, final String jvmClassName) { return findPsiClass(manager, jvmClassName.replace('/', '.'), null, true); diff --git a/java/testFramework/src/com/intellij/testFramework/fixtures/DefaultLightProjectDescriptor.java b/java/testFramework/src/com/intellij/testFramework/fixtures/DefaultLightProjectDescriptor.java index 44ce03bbb0ab..1dc3be12660e 100644 --- a/java/testFramework/src/com/intellij/testFramework/fixtures/DefaultLightProjectDescriptor.java +++ b/java/testFramework/src/com/intellij/testFramework/fixtures/DefaultLightProjectDescriptor.java @@ -42,6 +42,9 @@ public class DefaultLightProjectDescriptor implements LightProjectDescriptor { @Override public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) { - model.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(LanguageLevel.HIGHEST); + LanguageLevelModuleExtension extension = model.getModuleExtension(LanguageLevelModuleExtension.class); + if (extension != null) { + extension.setLanguageLevel(LanguageLevel.HIGHEST); + } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java index 2c642f0df771..4bfc4b9c294e 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java @@ -1,160 +1,10 @@ package org.jetbrains.jps.incremental; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.io.FileUtil; -import org.jetbrains.ether.dependencyView.Mappings; -import org.jetbrains.jps.Module; -import org.jetbrains.jps.ModuleChunk; -import org.jetbrains.jps.incremental.storage.SourceToOutputMapping; - -import java.io.File; -import java.io.IOException; -import java.util.*; - /** - * @author Eugene Zhuravlev - * Date: 9/17/11 + * @author nik */ public abstract class Builder { - private static final Key> ALL_AFFECTED_FILES_KEY = Key.create("_all_affected_files_"); - private static final Key> ALL_COMPILED_FILES_KEY = Key.create("_all_compiled_files_"); - - public static enum ExitCode { - OK, ABORT, ADDITIONAL_PASS_REQUIRED - } - public abstract String getName(); - public abstract ExitCode build(CompileContext context, ModuleChunk chunk) throws ProjectBuildException; - public abstract String getDescription(); - - public void cleanupResources(CompileContext context, ModuleChunk chunk) { - ALL_AFFECTED_FILES_KEY.set(context, null); - ALL_COMPILED_FILES_KEY.set(context, null); - } - - /** - * @param context - * @param delta - * @param chunk - * @param filesToCompile files compiled in this round - * @param successfullyCompiled - * @return true if additional compilation pass is required, false otherwise - * @throws Exception - */ - public final boolean updateMappings(CompileContext context, final Mappings delta, ModuleChunk chunk, Collection filesToCompile, Collection successfullyCompiled) throws Exception { - try { - boolean additionalPassRequired = false; - - final Set removedPaths = getRemovedPaths(context); - - final Mappings globalMappings = context.getDataManager().getMappings(); - - //noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized (globalMappings) { - if (!context.isProjectRebuild() && context.shouldDifferentiate(chunk, context.isCompilingTests())) { - final Set allCompiledFiles = getAllCompiledFilesContainer(context); - final Set allAffectedFiles = getAllAffectedFilesContainer(context); - - // mark as affected all files that were dirty before compilation - allAffectedFiles.addAll(filesToCompile); - // accumulate all successfully compiled in this round - allCompiledFiles.addAll(successfullyCompiled); - // unmark as affected all successfully compiled - allAffectedFiles.removeAll(successfullyCompiled); - - final HashSet affectedBeforeDif = new HashSet(allAffectedFiles); - - final boolean incremental = globalMappings.differentiate( - delta, removedPaths, successfullyCompiled, allCompiledFiles, allAffectedFiles - ); - - if (incremental) { - final Set newlyAffectedFiles = new HashSet(allAffectedFiles); - newlyAffectedFiles.removeAll(affectedBeforeDif); - newlyAffectedFiles.removeAll(allCompiledFiles); // the diff operation may have affected the class already compiled in thic compilation round - - if (!newlyAffectedFiles.isEmpty()) { - for (File file : newlyAffectedFiles) { - context.markDirty(file); - } - additionalPassRequired = context.isMake() && chunkContainsAffectedFiles(context, chunk, newlyAffectedFiles); - } - } - else { - additionalPassRequired = context.isMake(); - context.markDirtyRecursively(chunk); - } - } - - globalMappings.integrate(delta, successfullyCompiled, removedPaths); - } - - return additionalPassRequired; - } - catch(RuntimeException e) { - final Throwable cause = e.getCause(); - if (cause instanceof IOException) { - throw ((IOException)cause); - } - throw e; - } - } - - // delete all class files that according to mappings correspond to given sources - public static void deleteCorrespondingOutputFiles(CompileContext context, Map sources) throws Exception { - if (!context.isProjectRebuild() && !sources.isEmpty()) { - for (Map.Entry pair : sources.entrySet()) { - final File file = pair.getKey(); - final String srcPath = FileUtil.toSystemIndependentName(file.getPath()); - final String moduleName = pair.getValue().getName().toLowerCase(Locale.US); - final SourceToOutputMapping srcToOut = context.getDataManager().getSourceToOutputMap(moduleName, context.isCompilingTests()); - final Collection outputs = srcToOut.getState(srcPath); - if (outputs != null) { - for (String output : outputs) { - FileUtil.delete(new File(output)); - } - srcToOut.remove(srcPath); - } - } - } - } - - private static boolean chunkContainsAffectedFiles(CompileContext context, ModuleChunk chunk, final Set affected) throws Exception { - final Set chunkModules = new HashSet(chunk.getModules()); - if (!chunkModules.isEmpty()) { - for (File file : affected) { - final RootDescriptor moduleAndRoot = context.getModuleAndRoot(file); - if (moduleAndRoot != null && chunkModules.contains(moduleAndRoot.module)) { - return true; - } - } - } - return false; - } - - private static Set getAllAffectedFilesContainer(CompileContext context) { - Set allAffectedFiles = ALL_AFFECTED_FILES_KEY.get(context); - if (allAffectedFiles == null) { - allAffectedFiles = new HashSet(); - ALL_AFFECTED_FILES_KEY.set(context, allAffectedFiles); - } - return allAffectedFiles; - } - - private static Set getAllCompiledFilesContainer(CompileContext context) { - Set allCompiledFiles = ALL_COMPILED_FILES_KEY.get(context); - if (allCompiledFiles == null) { - allCompiledFiles = new HashSet(); - ALL_COMPILED_FILES_KEY.set(context, allCompiledFiles); - } - return allCompiledFiles; - } - - private static Set getRemovedPaths(CompileContext context) { - final Set removed = Paths.CHUNK_REMOVED_SOURCES_KEY.get(context); - return removed != null? removed : Collections.emptySet(); - } - } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java index 7840245b8409..51a128ddc6ff 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java @@ -2,7 +2,7 @@ package org.jetbrains.jps.incremental; import org.jetbrains.jps.incremental.groovy.GroovyBuilder; import org.jetbrains.jps.incremental.java.JavaBuilder; -import org.jetbrains.jps.incremental.resourses.ResourcesBuilder; +import org.jetbrains.jps.incremental.resources.ResourcesBuilder; import java.util.*; import java.util.concurrent.ExecutorService; @@ -16,7 +16,7 @@ public class BuilderRegistry { private static class Holder { static final BuilderRegistry ourInstance = new BuilderRegistry(); } - private final Map> myBuilders = new HashMap>(); + private final Map> myBuilders = new HashMap>(); private ExecutorService myTasksExecutor; public static BuilderRegistry getInstance() { @@ -25,7 +25,7 @@ public class BuilderRegistry { private BuilderRegistry() { for (BuilderCategory category : BuilderCategory.values()) { - myBuilders.put(category, new ArrayList()); + myBuilders.put(category, new ArrayList()); } final Runtime runtime = Runtime.getRuntime(); myTasksExecutor = Executors.newFixedThreadPool(runtime.availableProcessors()); @@ -60,7 +60,7 @@ public class BuilderRegistry { return Collections.emptyList(); // todo } - public List getBuilders(BuilderCategory category){ + public List getBuilders(BuilderCategory category){ return Collections.unmodifiableList(myBuilders.get(category)); // todo } 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 65efacbb9bcb..ccfa9e42dc4b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -314,7 +314,7 @@ public class IncProjectBuilder { finally { try { for (BuilderCategory category : BuilderCategory.values()) { - for (Builder builder : myBuilderRegistry.getBuilders(category)) { + for (ModuleLevelBuilder builder : myBuilderRegistry.getBuilders(category)) { builder.cleanupResources(context, chunk); } } @@ -334,7 +334,7 @@ public class IncProjectBuilder { } private void runBuilders(CompileContext context, ModuleChunk chunk, BuilderCategory category) throws ProjectBuildException { - final List builders = myBuilderRegistry.getBuilders(category); + final List builders = myBuilderRegistry.getBuilders(category); if (builders.isEmpty()) { return; } @@ -347,16 +347,16 @@ public class IncProjectBuilder { do { nextPassRequired = false; context.beforeNextCompileRound(chunk); - for (Builder builder : builders) { - final Builder.ExitCode buildResult = builder.build(context, chunk); + for (ModuleLevelBuilder builder : builders) { + final ModuleLevelBuilder.ExitCode buildResult = builder.build(context, chunk); - if (buildResult == Builder.ExitCode.ABORT) { + if (buildResult == ModuleLevelBuilder.ExitCode.ABORT) { throw new ProjectBuildException("Builder " + builder.getDescription() + " requested build stop"); } if (myCancelStatus.isCanceled()) { throw new ProjectBuildException(CANCELED_MESSAGE); } - if (buildResult == Builder.ExitCode.ADDITIONAL_PASS_REQUIRED) { + if (buildResult == ModuleLevelBuilder.ExitCode.ADDITIONAL_PASS_REQUIRED) { if (!nextPassRequired) { // recalculate basis myModulesProcessed -= (stagesPassed * modulesInChunk) / stageCount; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java new file mode 100644 index 000000000000..314187fa1100 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java @@ -0,0 +1,177 @@ +package org.jetbrains.jps.incremental; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.ether.dependencyView.Mappings; +import org.jetbrains.jps.Module; +import org.jetbrains.jps.ModuleChunk; +import org.jetbrains.jps.incremental.storage.SourceToOutputMapping; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * @author Eugene Zhuravlev + * Date: 9/17/11 + */ +public abstract class ModuleLevelBuilder extends Builder { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.Builder"); + + private static final Key> ALL_AFFECTED_FILES_KEY = Key.create("_all_affected_files_"); + private static final Key> ALL_COMPILED_FILES_KEY = Key.create("_all_compiled_files_"); + + public static enum ExitCode { + OK, ABORT, ADDITIONAL_PASS_REQUIRED + } + + public abstract ExitCode build(CompileContext context, ModuleChunk chunk) throws ProjectBuildException; + + public void cleanupResources(CompileContext context, ModuleChunk chunk) { + ALL_AFFECTED_FILES_KEY.set(context, null); + ALL_COMPILED_FILES_KEY.set(context, null); + } + + /** + * @param context + * @param delta + * @param chunk + * @param filesToCompile files compiled in this round + * @param successfullyCompiled + * @return true if additional compilation pass is required, false otherwise + * @throws Exception + */ + public final boolean updateMappings(CompileContext context, final Mappings delta, ModuleChunk chunk, Collection filesToCompile, Collection successfullyCompiled) throws Exception { + try { + boolean additionalPassRequired = false; + + final Set removedPaths = getRemovedPaths(context); + + final Mappings globalMappings = context.getDataManager().getMappings(); + + //noinspection SynchronizationOnLocalVariableOrMethodParameter + synchronized (globalMappings) { + if (!context.isProjectRebuild() && context.shouldDifferentiate(chunk, context.isCompilingTests())) { + final Set allCompiledFiles = getAllCompiledFilesContainer(context); + final Set allAffectedFiles = getAllAffectedFilesContainer(context); + + // mark as affected all files that were dirty before compilation + allAffectedFiles.addAll(filesToCompile); + // accumulate all successfully compiled in this round + allCompiledFiles.addAll(successfullyCompiled); + // unmark as affected all successfully compiled + allAffectedFiles.removeAll(successfullyCompiled); + + final HashSet affectedBeforeDif = new HashSet(allAffectedFiles); + + final boolean incremental = globalMappings.differentiate( + delta, removedPaths, successfullyCompiled, allCompiledFiles, allAffectedFiles + ); + + if (LOG.isDebugEnabled()) { + LOG.debug("Differentiate Results:"); + + LOG.debug(" Compiled Files:"); + + for (final File c : allCompiledFiles) { + LOG.debug(" " + c.getAbsolutePath()); + } + + LOG.debug(" Affected Files:"); + + for (final File c : allAffectedFiles) { + LOG.debug(" " + c.getAbsolutePath()); + } + + LOG.debug("End Of Differentiate Results."); + } + + if (incremental) { + final Set newlyAffectedFiles = new HashSet(allAffectedFiles); + newlyAffectedFiles.removeAll(affectedBeforeDif); + newlyAffectedFiles.removeAll(allCompiledFiles); // the diff operation may have affected the class already compiled in thic compilation round + + if (!newlyAffectedFiles.isEmpty()) { + for (File file : newlyAffectedFiles) { + context.markDirty(file); + } + additionalPassRequired = context.isMake() && chunkContainsAffectedFiles(context, chunk, newlyAffectedFiles); + } + } + else { + additionalPassRequired = context.isMake(); + context.markDirtyRecursively(chunk); + } + } + + globalMappings.integrate(delta, successfullyCompiled, removedPaths); + } + + return additionalPassRequired; + } + catch(RuntimeException e) { + final Throwable cause = e.getCause(); + if (cause instanceof IOException) { + throw ((IOException)cause); + } + throw e; + } + } + + // delete all class files that according to mappings correspond to given sources + public static void deleteCorrespondingOutputFiles(CompileContext context, Map sources) throws Exception { + if (!context.isProjectRebuild() && !sources.isEmpty()) { + for (Map.Entry pair : sources.entrySet()) { + final File file = pair.getKey(); + final String srcPath = FileUtil.toSystemIndependentName(file.getPath()); + final String moduleName = pair.getValue().getName().toLowerCase(Locale.US); + final SourceToOutputMapping srcToOut = context.getDataManager().getSourceToOutputMap(moduleName, context.isCompilingTests()); + final Collection outputs = srcToOut.getState(srcPath); + if (outputs != null) { + for (String output : outputs) { + FileUtil.delete(new File(output)); + } + srcToOut.remove(srcPath); + } + } + } + } + + private static boolean chunkContainsAffectedFiles(CompileContext context, ModuleChunk chunk, final Set affected) throws Exception { + final Set chunkModules = new HashSet(chunk.getModules()); + if (!chunkModules.isEmpty()) { + for (File file : affected) { + final RootDescriptor moduleAndRoot = context.getModuleAndRoot(file); + if (moduleAndRoot != null && chunkModules.contains(moduleAndRoot.module)) { + return true; + } + } + } + return false; + } + + private static Set getAllAffectedFilesContainer(CompileContext context) { + Set allAffectedFiles = ALL_AFFECTED_FILES_KEY.get(context); + if (allAffectedFiles == null) { + allAffectedFiles = new HashSet(); + ALL_AFFECTED_FILES_KEY.set(context, allAffectedFiles); + } + return allAffectedFiles; + } + + private static Set getAllCompiledFilesContainer(CompileContext context) { + Set allCompiledFiles = ALL_COMPILED_FILES_KEY.get(context); + if (allCompiledFiles == null) { + allCompiledFiles = new HashSet(); + ALL_COMPILED_FILES_KEY.set(context, allCompiledFiles); + } + return allCompiledFiles; + } + + private static Set getRemovedPaths(CompileContext context) { + final Set removed = Paths.CHUNK_REMOVED_SOURCES_KEY.get(context); + return removed != null? removed : Collections.emptySet(); + } + +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ProjectLevelBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ProjectLevelBuilder.java new file mode 100644 index 000000000000..c8296590c3aa --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ProjectLevelBuilder.java @@ -0,0 +1,20 @@ +package org.jetbrains.jps.incremental; + +/** + * @author nik + */ +public abstract class ProjectLevelBuilder extends Builder { + private final ProjectLevelBuilderCategory myCategory; + + protected ProjectLevelBuilder(ProjectLevelBuilderCategory category) { + myCategory = category; + } + + public abstract void build(CompileContext context); + + public ProjectLevelBuilderCategory getCategory() { + return myCategory; + } + + public static enum ProjectLevelBuilderCategory { TRANSLATOR, PACKAGER } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java index 2bb3e3c411ab..7f8be2cb10a2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java @@ -29,7 +29,7 @@ import java.util.*; * @author Eugene Zhuravlev * Date: 10/25/11 */ -public class GroovyBuilder extends Builder { +public class GroovyBuilder extends ModuleLevelBuilder { public static final String BUILDER_NAME = "groovy"; private final boolean myForStubs; private final String myBuilderName; @@ -43,7 +43,7 @@ public class GroovyBuilder extends Builder { return myBuilderName; } - public Builder.ExitCode build(final CompileContext context, ModuleChunk chunk) throws ProjectBuildException { + public ModuleLevelBuilder.ExitCode build(final CompileContext context, ModuleChunk chunk) throws ProjectBuildException { ExitCode exitCode = ExitCode.OK; final Map toCompile = new HashMap(); try { @@ -104,12 +104,13 @@ public class GroovyBuilder extends Builder { // todo CompilerUtil.addLocaleOptions() //todo different outputs in a chunk + //todo xmx //todo module jdk path final List cmd = ExternalProcessUtil.buildJavaCommandLine( SystemProperties.getJavaHome() + "/bin/java", "org.jetbrains.groovy.compiler.rt.GroovycRunner", Collections.emptyList(), new ArrayList(cp), - Arrays.asList("-Xmx384m"/*, "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5858"*/), + Arrays.asList("-Xmx384m"/*, "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239"*/), Arrays.asList(myForStubs ? "stubs" : "groovyc", tempFile.getPath()) ); 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 1be97a81fcf4..2269278c3908 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 @@ -49,7 +49,7 @@ import java.util.concurrent.ExecutorService; * @author Eugene Zhuravlev * Date: 9/21/11 */ -public class JavaBuilder extends Builder{ +public class JavaBuilder extends ModuleLevelBuilder { public static final String BUILDER_NAME = "java"; private static final String JAVA_EXTENSION = ".java"; private static final String FORM_EXTENSION = ".form"; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/resourses/ResourcesBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/resources/ResourcesBuilder.java similarity index 97% rename from jps/jps-builders/src/org/jetbrains/jps/incremental/resourses/ResourcesBuilder.java rename to jps/jps-builders/src/org/jetbrains/jps/incremental/resources/ResourcesBuilder.java index a8b7725d3fd4..a318f8ef4dd7 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/resourses/ResourcesBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/resources/ResourcesBuilder.java @@ -1,4 +1,4 @@ -package org.jetbrains.jps.incremental.resourses; +package org.jetbrains.jps.incremental.resources; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; @@ -19,7 +19,7 @@ import java.util.Locale; * @author Eugene Zhuravlev * Date: 10/6/11 */ -public class ResourcesBuilder extends Builder{ +public class ResourcesBuilder extends ModuleLevelBuilder { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.resourses.ResourcesBuilder"); public static final String BUILDER_NAME = "resources"; diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/DependencyContext.java b/jps/model/src/org/jetbrains/ether/dependencyView/DependencyContext.java index c3c796f349de..75756ad6f05e 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/DependencyContext.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/DependencyContext.java @@ -172,9 +172,23 @@ class DependencyContext { public Logger getLogger(final com.intellij.openapi.diagnostic.Logger log) { return new Logger() { @Override - public void debug(S s) { + public void debug(String comment, S s) { if (log.isDebugEnabled()) { - log.debug(getValue(s)); + log.debug(comment + getValue(s)); + } + } + + @Override + public void debug(String comment, String t) { + if (log.isDebugEnabled()){ + log.debug(comment + t); + } + } + + @Override + public void debug(String comment, boolean t) { + if (log.isDebugEnabled()) { + log.debug(comment + Boolean.toString(t)); } } }; diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/Logger.java b/jps/model/src/org/jetbrains/ether/dependencyView/Logger.java index 20ffdf19d8f4..a409a4c9ec6f 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/Logger.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/Logger.java @@ -23,5 +23,7 @@ package org.jetbrains.ether.dependencyView; * To change this template use File | Settings | File Templates. */ public interface Logger { - void debug(T t); + void debug(String comment, T t); + void debug(String comment, String t); + void debug(String comment, boolean t); } diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java index 43bde0ea86f3..16846ac8e8f2 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java @@ -1,13 +1,12 @@ package org.jetbrains.ether.dependencyView; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.annotations.Nullable; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; -import com.intellij.openapi.diagnostic.Logger; -import org.jetbrains.annotations.*; - import java.io.File; import java.io.IOException; import java.lang.annotation.ElementType; @@ -22,7 +21,7 @@ import java.util.*; * To change this template use File | Settings | File Templates. */ public class Mappings { - private final static Logger LOG = Logger.getInstance ("#org.jetbrains.ether.dependencyView.Mappings"); + private final static Logger LOG = Logger.getInstance("#org.jetbrains.ether.dependencyView.Mappings"); private final static String CLASS_TO_SUBCLASSES = "classToSubclasses.tab"; private final static String CLASS_TO_CLASS = "classToClass.tab"; @@ -38,12 +37,20 @@ public class Mappings { private DependencyContext myContext; private org.jetbrains.ether.dependencyView.Logger myDebugS; - private void debug(final String s) { + private static void debug(final String s) { LOG.debug(s); } - private void debug(final DependencyContext.S s) { - myDebugS.debug(s); + private void debug(final String comment, final DependencyContext.S s) { + myDebugS.debug(comment, s); + } + + private void debug(final String comment, final String s) { + myDebugS.debug(comment, s); + } + + private void debug(final String comment, final boolean s) { + myDebugS.debug(comment, s); } private MultiMaplet myClassToSubclasses; @@ -534,16 +541,25 @@ public class Mappings { final Collection affectedUsages, final Collection dependants, final boolean usages) { + debug("Affecting subclasses of class: ", className); + final DependencyContext.S fileName = myClassToSourceFile.get(className); if (fileName == null) { + debug("No source file detected for class ", className); + debug("End of affectSubclasses"); return; } + debug("Source file name: ", fileName); + if (usages) { + debug("Class usages affection requested"); + final ClassRepr classRepr = reprByName(className); if (classRepr != null) { + debug("Added class usage for ", classRepr.name); affectedUsages.add(classRepr.createUsage()); } } @@ -579,6 +595,7 @@ public class Mappings { dependents.addAll(deps); } + debug("Affect field usage referenced of class ", p); affectedUsages .add(rootUsage instanceof UsageRepr.FieldAssignUsage ? field.createAssignUsage(myContext, p) : field.createUsage(myContext, p)); } @@ -598,6 +615,8 @@ public class Mappings { dependents.addAll(deps); } + debug("Affect method usage referenced of class ", p); + affectedUsages .add(rootUsage instanceof UsageRepr.MetaMethodUsage ? method.createMetaUsage(myContext, p) : method.createUsage(myContext, p)); } @@ -730,32 +749,38 @@ public class Mappings { // Public branch --- hopeless if ((member.access & Opcodes.ACC_PUBLIC) > 0) { - debug("Switched to non-incremental mode"); + debug("Public access, switching to a non-incremental mode"); return false; } // Protected branch if ((member.access & Opcodes.ACC_PROTECTED) > 0) { - debug("Softening non-incremental decision: adding all relevant subclasses for a recompilation"); + debug("Protected access, softening non-incremental decision: adding all relevant subclasses for a recompilation"); + debug("Root class: ", owner); final Collection propagated = self.propagateFieldAccess(isField ? member.name : myContext.get(""), owner); for (DependencyContext.S className : propagated) { - affectedFiles.add(new File(myContext.getValue(myClassToSourceFile.get(className)))); + final String fileName = myContext.getValue(myClassToSourceFile.get(className)); + debug("Adding ", fileName); + affectedFiles.add(new File(fileName)); } } - debug("Softening non-incremental decision: adding all package classes for a recompilation"); - final String packageName = ClassRepr.getPackageName(myContext.getValue(isField ? owner : member.name)); + debug("Softening non-incremental decision: adding all package classes for a recompilation"); + debug("Package name: ", packageName); + // Package-local branch for (Map.Entry e : myClassToSourceFile.entrySet()) { final DependencyContext.S className = e.getKey(); final DependencyContext.S fileName = e.getValue(); if (ClassRepr.getPackageName(myContext.getValue(className)).equals(packageName)) { - affectedFiles.add(new File(myContext.getValue(fileName))); + final String f = myContext.getValue(fileName); + debug("Adding: ", f); + affectedFiles.add(new File(f)); } } @@ -767,6 +792,8 @@ public class Mappings { final Collection filesToCompile, final Collection compiledFiles, final Collection affectedFiles) { + debug("Begin of Differentiate:"); + delta.compensateRemovedContent(filesToCompile); final Util u = new Util(delta); @@ -798,14 +825,12 @@ public class Mappings { final Difference.Specifier classDiff = Difference.make(pastClasses, classes); - debug("Processing changed classes"); - + debug("Processing changed classes:"); for (Pair changed : classDiff.changed()) { final ClassRepr it = changed.first; final ClassRepr.Diff diff = (ClassRepr.Diff)changed.second; - debug("Changed: "); - debug(it.name); + debug("Changed: ", it.name); final int addedModifiers = diff.addedModifiers(); final int removedModifiers = diff.removedModifiers(); @@ -815,24 +840,34 @@ public class Mappings { final boolean signatureChanged = (diff.base() & Difference.SIGNATURE) > 0; if (superClassChanged || interfacesChanged || signatureChanged) { + debug("Superclass changed: ", superClassChanged); + debug("Interfaces changed: ", interfacesChanged); + debug("Signature changed ", signatureChanged); + final boolean extendsChanged = superClassChanged && !diff.extendsAdded(); final boolean interfacesRemoved = interfacesChanged && !diff.interfaces().removed().isEmpty(); + debug("Extends changed: ", extendsChanged); + debug("Interfaces removed: ", interfacesRemoved); + u.affectSubclasses(it.name, affectedFiles, affectedUsages, dependants, extendsChanged || interfacesRemoved || signatureChanged); } if ((diff.addedModifiers() & Opcodes.ACC_INTERFACE) > 0 || (diff.removedModifiers() & Opcodes.ACC_INTERFACE) > 0) { + debug("Class-to-interface or interface-to-class conversion detected, added class usage to affected usages"); affectedUsages.add(it.createUsage()); } if (it.isAnnotation() && it.policy == RetentionPolicy.SOURCE) { debug("Annotation, retention policy = SOURCE => a switch to non-incremental mode requested"); if (!incrementalDecision(it.outerClassName, it, affectedFiles)) { + debug("End of Differentiate, returning false"); return false; } } if ((addedModifiers & Opcodes.ACC_PROTECTED) > 0) { + debug("Introduction of 'protected' modifier detected, adding class usage + inheritance constraint to affected usages"); final UsageRepr.Usage usage = it.createUsage(); affectedUsages.add(usage); @@ -840,6 +875,7 @@ public class Mappings { } if (diff.packageLocalOn()) { + debug("Introduction of 'package local' access detected, adding class usage + package constraint to affected usages"); final UsageRepr.Usage usage = it.createUsage(); affectedUsages.add(usage); @@ -847,54 +883,64 @@ public class Mappings { } if ((addedModifiers & Opcodes.ACC_FINAL) > 0 || (addedModifiers & Opcodes.ACC_PRIVATE) > 0) { + debug("Introduction of 'private' or 'final' modifier(s) detected, adding class usage to affected usages"); affectedUsages.add(it.createUsage()); } - if ((addedModifiers & Opcodes.ACC_ABSTRACT) > 0) { - affectedUsages.add(UsageRepr.createClassNewUsage(myContext, it.name)); - } - - if ((addedModifiers & Opcodes.ACC_STATIC) > 0 || - (removedModifiers & Opcodes.ACC_STATIC) > 0 || - (addedModifiers & Opcodes.ACC_ABSTRACT) > 0) { + if ((addedModifiers & Opcodes.ACC_ABSTRACT) > 0 || (addedModifiers & Opcodes.ACC_STATIC) > 0) { + debug("Introduction of 'abstract' or 'static' modifier(s) detected, adding class new usage to affected usages"); affectedUsages.add(UsageRepr.createClassNewUsage(myContext, it.name)); } if (it.isAnnotation()) { + debug("Class is annotation, performing annotation-specific analysis"); + if (diff.retentionChanged()) { + debug("Retention policy change detected, adding class usage to affected usages"); affectedUsages.add(it.createUsage()); } else { final Collection removedtargets = diff.targets().removed(); if (removedtargets.contains(ElementType.LOCAL_VARIABLE)) { - debug("Annotation, removed target contains LOCAL_VARIABLE => a switch to non-incremental mode requested"); + debug("Removed target contains LOCAL_VARIABLE => a switch to non-incremental mode requested"); if (!incrementalDecision(it.outerClassName, it, affectedFiles)) { + debug("End of Differentiate, returning false"); return false; } } if (!removedtargets.isEmpty()) { + debug("Removed some annotation targets, adding annotation query"); annotationQuery.add((UsageRepr.AnnotationUsage)UsageRepr .createAnnotationUsage(myContext, TypeRepr.createClassType(myContext, it.name), null, removedtargets)); } for (MethodRepr m : diff.methods().added()) { if (!m.hasValue()) { + debug("Added method with no default value: ", m.name); + debug("Adding class usage to affected usages"); affectedUsages.add(it.createUsage()); } } } + + debug("End of annotation-specific analysis"); } + debug("Processing added methods: "); for (MethodRepr m : diff.methods().added()) { + debug("Method: ", m.name); + if (it.isAnnotation()) { + debug("Class is annotation, skipping method analysis"); continue; } if ((it.access & Opcodes.ACC_INTERFACE) > 0 || (it.access & Opcodes.ACC_ABSTRACT) > 0 || (m.access & Opcodes.ACC_ABSTRACT) > 0) { + debug("Class is abstract, or is interface, or added method in abstract => affecting all subclasses"); u.affectSubclasses(it.name, affectedFiles, affectedUsages, dependants, false); } @@ -908,6 +954,7 @@ public class Mappings { } else { propagated = u.propagateMethodAccess(m.name, it.name); + debug("Conservative case on overriding methods, affecting method usages"); u.affectMethodUsages(m, propagated, m.createMetaUsage(myContext, it.name), affectedUsages, dependants); } } @@ -924,10 +971,12 @@ public class Mappings { for (MethodRepr mm : lessSpecific) { if (!mm.equals(m)) { + debug("Found less specific method, affecting method usages"); u.affectMethodUsages(mm, propagated, mm.createUsage(myContext, it.name), affectedUsages, dependants); } } + debug("Processing affected by specificity methods"); for (Pair p : affectedMethods) { final MethodRepr mm = p.first; final ClassRepr cc = p.second; @@ -936,7 +985,12 @@ public class Mappings { } else { + debug("Method: ", mm.name); + debug("Class : ", cc.name); + if (overrides.satisfy(mm)) { + debug("Current method overrides that found"); + final Option subtypeOf = u.isSubtypeOf(mm.type, m.type); if (weakerAccess(mm.access, m.access) || @@ -949,11 +1003,15 @@ public class Mappings { final DependencyContext.S file = myClassToSourceFile.get(cc.name); if (file != null) { - affectedFiles.add(new File(myContext.getValue(file))); + final String f = myContext.getValue(file); + debug("Complex condition is satisfied, affecting file ", f); + affectedFiles.add(new File(f)); } } } else { + debug("Current method does not override that found"); + final Collection yetPropagated = self.propagateMethodAccess(mm.name, cc.name); final Collection deps = myClassToClassDependency.get(cc.name); @@ -961,6 +1019,7 @@ public class Mappings { dependants.addAll(deps); } + debug("Affecting method usages for that found"); u.affectMethodUsages(mm, yetPropagated, mm.createUsage(myContext, cc.name), affectedUsages, dependants); } } @@ -977,19 +1036,26 @@ public class Mappings { final DependencyContext.S outerClass = r.outerClassName; if (u.methodVisible(outerClass, m)) { - affectedFiles.add(new File(myContext.getValue(sourceFileName))); + final String f = myContext.getValue(sourceFileName); + debug("Affecting file due to local overriding: ", f); + affectedFiles.add(new File(f)); } } } } } } + debug("End of added methods processing"); + debug("Processing removed methods:"); for (MethodRepr m : diff.methods().removed()) { + debug("Method ", m.name); + final Collection> overridenMethods = u.findOverridenMethods(m, it); final Collection propagated = u.propagateMethodAccess(m.name, it.name); if (overridenMethods.size() == 0) { + debug("No overridden methods found, affecting method usages"); u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), affectedUsages, dependants); } else { @@ -1006,6 +1072,7 @@ public class Mappings { } if (!clear) { + debug("No clearly overridden methods found, affecting method usages"); u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), affectedUsages, dependants); } } @@ -1046,21 +1113,31 @@ public class Mappings { final DependencyContext.S source = myClassToSourceFile.get(p); if (source != null) { - affectedFiles.add(new File(myContext.getValue(source))); + final String f = myContext.getValue(source); + debug( + "Removed method is not abstract & is overrides some abstract method which is not then over-overriden in subclass ", + p); + debug("Affecting subclass source file ", f); + affectedFiles.add(new File(f)); } } } } } } + debug("End of removed methods processing"); + debug("Processing changed methods:"); for (Pair mr : diff.methods().changed()) { final MethodRepr m = mr.first; final MethodRepr.Diff d = (MethodRepr.Diff)mr.second; final boolean throwsChanged = (d.exceptions().added().size() > 0) || (d.exceptions().changed().size() > 0); + debug("Method: ", m.name); + if (it.isAnnotation()) { if (d.defaultRemoved()) { + debug("Class is annotation, default value is removed => adding annotation query"); final List l = new LinkedList(); l.add(m.name); annotationQuery.add((UsageRepr.AnnotationUsage)UsageRepr @@ -1076,6 +1153,7 @@ public class Mappings { final Set usages = new HashSet(); if (d.packageLocalOn()) { + debug("Method became package-local, affecting method usages outside the package"); u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, dependants); for (UsageRepr.Usage usage : usages) { @@ -1089,6 +1167,7 @@ public class Mappings { if ((d.base() & Difference.TYPE) > 0 || (d.base() & Difference.SIGNATURE) > 0 || throwsChanged) { if (!affected) { + debug("Return type, throws list or signature changed --- affecting method usages"); u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, dependants); affectedUsages.addAll(usages); } @@ -1098,11 +1177,13 @@ public class Mappings { (d.removedModifiers() & Opcodes.ACC_STATIC) > 0 || (d.addedModifiers() & Opcodes.ACC_PRIVATE) > 0) { if (!affected) { + debug("Added static or private specifier or removed static specifier --- affecting method usages"); u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, dependants); affectedUsages.addAll(usages); } if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0) { + debug("Added static specifier --- affecting subclasses"); u.affectSubclasses(it.name, affectedFiles, affectedUsages, dependants, false); } } @@ -1110,11 +1191,13 @@ public class Mappings { if ((d.addedModifiers() & Opcodes.ACC_FINAL) > 0 || (d.addedModifiers() & Opcodes.ACC_PUBLIC) > 0 || (d.addedModifiers() & Opcodes.ACC_ABSTRACT) > 0) { + debug("Added final, public or abstract specifier --- affecting subclasses"); u.affectSubclasses(it.name, affectedFiles, affectedUsages, dependants, false); } if ((d.addedModifiers() & Opcodes.ACC_PROTECTED) > 0 && !((d.removedModifiers() & Opcodes.ACC_PRIVATE) > 0)) { if (!constrained) { + debug("Added public or package-local method became protected --- affect method usages with protected constraint"); if (!affected) { u.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, dependants); affectedUsages.addAll(usages); @@ -1129,10 +1212,14 @@ public class Mappings { } } } + debug("End of changed methods processing"); final int mask = Opcodes.ACC_STATIC | Opcodes.ACC_FINAL; + debug("Processing added fields"); for (FieldRepr f : diff.fields().added()) { + debug("Field: ", f.name); + final boolean fPrivate = (f.access & Opcodes.ACC_PRIVATE) > 0; final boolean fProtected = (f.access & Opcodes.ACC_PROTECTED) > 0; final boolean fPublic = (f.access & Opcodes.ACC_PUBLIC) > 0; @@ -1141,32 +1228,34 @@ public class Mappings { if (!fPrivate) { final Collection subClasses = getAllSubclasses(it.name); - if (subClasses != null) { - for (final DependencyContext.S subClass : subClasses) { - final ClassRepr r = u.reprByName(subClass); - final DependencyContext.S sourceFileName = myClassToSourceFile.get(subClass); + for (final DependencyContext.S subClass : subClasses) { + final ClassRepr r = u.reprByName(subClass); + final DependencyContext.S sourceFileName = myClassToSourceFile.get(subClass); - if (r != null && sourceFileName != null) { - if (r.isLocal) { + if (r != null && sourceFileName != null) { + if (r.isLocal) { + debug("Affecting local subclass (introduced field can potentially hide surrounding method parameters/local variables): ", + sourceFileName); + affectedFiles.add(new File(myContext.getValue(sourceFileName))); + } + else { + final DependencyContext.S outerClass = r.outerClassName; + + if (!empty(outerClass) && u.fieldVisible(outerClass, f)) { + debug("Affecting inner subclass (introduced field can potentially hide surrounding class fields): ", sourceFileName); affectedFiles.add(new File(myContext.getValue(sourceFileName))); } - else { - final DependencyContext.S outerClass = r.outerClassName; - - if (!empty(outerClass) && u.fieldVisible(outerClass, f)) { - affectedFiles.add(new File(myContext.getValue(sourceFileName))); - } - } } + } - final Collection propagated = u.propagateFieldAccess(f.name, subClass); - u.affectFieldUsages(f, propagated, f.createUsage(myContext, subClass), affectedUsages, dependants); + debug("Affecting field usages referenced from subclass ", subClass); + final Collection propagated = u.propagateFieldAccess(f.name, subClass); + u.affectFieldUsages(f, propagated, f.createUsage(myContext, subClass), affectedUsages, dependants); - final Collection deps = myClassToClassDependency.get(subClass); + final Collection deps = myClassToClassDependency.get(subClass); - if (deps != null) { - dependants.addAll(deps); - } + if (deps != null) { + dependants.addAll(deps); } } } @@ -1186,6 +1275,7 @@ public class Mappings { final Collection propagated = o.propagateFieldAccess(ff.name, cc.name); final Set localUsages = new HashSet(); + debug("Affecting usages of overridden field in class ", cc.name); u.affectFieldUsages(ff, propagated, ff.createUsage(myContext, cc.name), localUsages, dependants); if (fPrivate || (fPublic && (ffPublic || ffPLocal)) || (fProtected && ffProtected) || (fPLocal && ffPLocal)) { @@ -1214,16 +1304,16 @@ public class Mappings { } } } + debug("End of added fields processing"); - debug("Processing removed fields"); - + debug("Processing removed fields:"); for (FieldRepr f : diff.fields().removed()) { - debug("Field "); - debug(f.name); + debug("Field: ", it.name); if ((f.access & Opcodes.ACC_PRIVATE) == 0 && (f.access & mask) == mask && f.hasValue()) { debug("Field had value and was (non-private) final static => a switch to non-incremental mode requested"); if (!incrementalDecision(it.name, f, affectedFiles)) { + debug("End of Differentiate, returning false"); return false; } } @@ -1231,20 +1321,20 @@ public class Mappings { final Collection propagated = u.propagateFieldAccess(f.name, it.name); u.affectFieldUsages(f, propagated, f.createUsage(myContext, it.name), affectedUsages, dependants); } + debug("End of removed fields processing"); - debug("Processing changed fields"); - + debug("Processing changed fields:"); for (Pair f : diff.fields().changed()) { final Difference d = f.second; final FieldRepr field = f.first; - debug("Field "); - debug(field.name); + debug("Field: ", it.name); if ((field.access & Opcodes.ACC_PRIVATE) == 0 && (field.access & mask) == mask) { if ((d.base() & Difference.ACCESS) > 0 || (d.base() & Difference.VALUE) > 0) { debug("Inline field changed it's access or value => a switch to non-incremental mode requested"); - if (!incrementalDecision(it.name, field, affectedFiles)){ + if (!incrementalDecision(it.name, field, affectedFiles)) { + debug("End of Differentiate, returning false"); return false; } } @@ -1254,6 +1344,7 @@ public class Mappings { final Collection propagated = u.propagateFieldAccess(field.name, it.name); if ((d.base() & Difference.TYPE) > 0 || (d.base() & Difference.SIGNATURE) > 0) { + debug("Type or signature changed --- affecting field usages"); u.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), affectedUsages, dependants); } else if ((d.base() & Difference.ACCESS) > 0) { @@ -1261,7 +1352,7 @@ public class Mappings { (d.removedModifiers() & Opcodes.ACC_STATIC) > 0 || (d.addedModifiers() & Opcodes.ACC_PRIVATE) > 0 || (d.addedModifiers() & Opcodes.ACC_VOLATILE) > 0) { - + debug("Added/removed static modifier or added private/volatile modifier --- affecting field usages"); u.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), affectedUsages, dependants); } else { @@ -1269,12 +1360,14 @@ public class Mappings { final Set usages = new HashSet(); if ((d.addedModifiers() & Opcodes.ACC_FINAL) > 0) { + debug("Added final modifier --- affecting field assign usages"); u.affectFieldUsages(field, propagated, field.createAssignUsage(myContext, it.name), usages, dependants); affectedUsages.addAll(usages); affected = true; } if ((d.removedModifiers() & Opcodes.ACC_PUBLIC) > 0) { + debug("Removed public modifier, affecting field usages with appropriate constraint"); if (!affected) { u.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), usages, dependants); affectedUsages.addAll(usages); @@ -1293,12 +1386,18 @@ public class Mappings { } } } + debug("End of changed fields processing"); } + debug("End of changed classes processing"); + debug("Processing removed classes:"); for (ClassRepr c : classDiff.removed()) { + debug("Adding usages of class ", c.name); affectedUsages.add(c.createUsage()); } + debug("End of removed classes processing."); + debug("Processing added classes:"); for (ClassRepr c : classDiff.added()) { final Collection depClasses = myClassToClassDependency.get(c.name); @@ -1307,12 +1406,16 @@ public class Mappings { final DependencyContext.S fName = myClassToSourceFile.get(depClass); if (fName != null) { - affectedFiles.add(new File(myContext.getValue(fName))); + final String f = myContext.getValue(fName); + debug("Adding dependent file ", f); + affectedFiles.add(new File(f)); } } } } + debug("End of added classes processing."); + debug("Checking dependent files:"); if (dependants != null) { final Set dependentFiles = new HashSet(); @@ -1332,6 +1435,8 @@ public class Mappings { continue filewise; } + debug("Dependent file: ", depFile); + final Collection depClusters = mySourceFileToUsages.get(depFile); for (UsageRepr.Cluster depCluster : depClusters) { @@ -1347,6 +1452,7 @@ public class Mappings { final Util.UsageConstraint constraint = usageConstraints.get(usage); if (constraint == null) { + debug("Added file with no constraints"); affectedFiles.add(theFile); continue filewise; } @@ -1354,6 +1460,7 @@ public class Mappings { final Set residenceClasses = depCluster.getResidence(usage); for (DependencyContext.S residentName : residenceClasses) { if (constraint.checkResidence(residentName)) { + debug("Added file with satisfied constraint"); affectedFiles.add(theFile); continue filewise; } @@ -1369,6 +1476,7 @@ public class Mappings { for (UsageRepr.Usage usage : annotationUsages) { for (UsageRepr.AnnotationUsage query : annotationQuery) { if (query.satisfies(usage)) { + debug("Added file due to annotation query"); affectedFiles.add(theFile); continue filewise; } @@ -1381,6 +1489,13 @@ public class Mappings { } } + if (removed != null) { + for (String r : removed) { + affectedFiles.remove(new File(r)); + } + } + + debug("End of Differentiate, returning true"); return true; } @@ -1425,7 +1540,6 @@ public class Mappings { } } - myClassToSubclasses.putAll(delta.myClassToSubclasses); mySourceFileToClasses.putAll(delta.mySourceFileToClasses); mySourceFileToUsages.putAll(delta.mySourceFileToUsages); diff --git a/lib/netty-3.2.5.Final.jar b/lib/netty-3.2.5.Final.jar deleted file mode 100644 index f3b79fcbaa75..000000000000 Binary files a/lib/netty-3.2.5.Final.jar and /dev/null differ diff --git a/lib/netty-3.3.0.Final.jar b/lib/netty-3.3.0.Final.jar new file mode 100644 index 000000000000..7929a276f775 Binary files /dev/null and b/lib/netty-3.3.0.Final.jar differ diff --git a/lib/required_for_dist.txt b/lib/required_for_dist.txt index 6e5ed206d18f..5a2fd8ed757e 100644 --- a/lib/required_for_dist.txt +++ b/lib/required_for_dist.txt @@ -44,5 +44,5 @@ xmlrpc-2.0.jar xpp3-1.1.4-min.jar xstream.jar swingx-core-1.6.2.jar -netty-3.2.5.Final.jar +netty-3.3.0.Final.jar protobuf-2.3.0.jar diff --git a/lib/src/netty-3.2.5.Final-sources.jar b/lib/src/netty-3.2.5.Final-sources.jar deleted file mode 100644 index cb6cb9bf537e..000000000000 Binary files a/lib/src/netty-3.2.5.Final-sources.jar and /dev/null differ diff --git a/lib/src/netty-3.3.0.Final-sources.jar b/lib/src/netty-3.3.0.Final-sources.jar new file mode 100644 index 000000000000..40d5a4efc500 Binary files /dev/null and b/lib/src/netty-3.3.0.Final-sources.jar differ diff --git a/platform/lang-api/src/com/intellij/execution/ui/actions/BaseViewAction.java b/platform/lang-api/src/com/intellij/execution/ui/actions/BaseViewAction.java index 869491c8a685..06c8cd55a7d7 100644 --- a/platform/lang-api/src/com/intellij/execution/ui/actions/BaseViewAction.java +++ b/platform/lang-api/src/com/intellij/execution/ui/actions/BaseViewAction.java @@ -17,7 +17,6 @@ package com.intellij.execution.ui.actions; import com.intellij.execution.ui.layout.Grid; -import com.intellij.execution.ui.layout.GridCell; import com.intellij.execution.ui.layout.Tab; import com.intellij.execution.ui.layout.ViewContext; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -74,11 +73,6 @@ public abstract class BaseViewAction extends DumbAwareAction { return e.getData(ViewContext.CONTENT_KEY); } - protected static boolean isDetached(ViewContext context, Content content) { - final GridCell cell = context.findCellFor(content); - return cell != null && cell.isDetached(); - } - @Nullable protected static Tab getTabFor(final ViewContext context, final Content[] content) { Grid grid = context.findGridFor(content[0]); diff --git a/platform/lang-api/src/com/intellij/execution/ui/layout/CellTransform.java b/platform/lang-api/src/com/intellij/execution/ui/layout/CellTransform.java index e37c46a19083..b9c7b21da197 100644 --- a/platform/lang-api/src/com/intellij/execution/ui/layout/CellTransform.java +++ b/platform/lang-api/src/com/intellij/execution/ui/layout/CellTransform.java @@ -72,12 +72,6 @@ public interface CellTransform { interface Facade { void minimize(Content content, Restore restore); - - void moveToTab(final Content content); - - void moveToGrid(final Content content); - - Restore detach(final Content[] content); } } diff --git a/platform/lang-api/src/com/intellij/execution/ui/layout/GridCell.java b/platform/lang-api/src/com/intellij/execution/ui/layout/GridCell.java index 2201bd8d6699..ebcbdd6a2a51 100644 --- a/platform/lang-api/src/com/intellij/execution/ui/layout/GridCell.java +++ b/platform/lang-api/src/com/intellij/execution/ui/layout/GridCell.java @@ -16,17 +16,10 @@ package com.intellij.execution.ui.layout; -import com.intellij.openapi.util.ActionCallback; import com.intellij.ui.content.Content; public interface GridCell { - boolean isDetached(); - int getContentCount(); - void attach(); - void minimize(final Content content); - - ActionCallback detach(); } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/execution/ui/layout/Tab.java b/platform/lang-api/src/com/intellij/execution/ui/layout/Tab.java index c208e3c49b1f..686162a17391 100644 --- a/platform/lang-api/src/com/intellij/execution/ui/layout/Tab.java +++ b/platform/lang-api/src/com/intellij/execution/ui/layout/Tab.java @@ -22,6 +22,7 @@ public interface Tab { void setDetached(final PlaceInGrid placeInGrid, final boolean detached); int getIndex(); + int getDefaultIndex(); boolean isDetached(final PlaceInGrid placeInGrid); } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/execution/ui/layout/View.java b/platform/lang-api/src/com/intellij/execution/ui/layout/View.java index 761a1b502250..c81532e8105f 100644 --- a/platform/lang-api/src/com/intellij/execution/ui/layout/View.java +++ b/platform/lang-api/src/com/intellij/execution/ui/layout/View.java @@ -32,4 +32,8 @@ public interface View { void assignTab(Tab tab); void setTabIndex(int tabIndex); + + int getWindow(); + + void setWindow(int windowNumber); } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java b/platform/lang-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java index 3c87dda6701c..8f777e4864c9 100644 --- a/platform/lang-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java +++ b/platform/lang-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java @@ -51,29 +51,29 @@ public class DefaultWordsScanner implements WordsScanner { public void processWords(CharSequence fileText, Processor processor) { myLexer.start(fileText); - WordOccurrence occurence = null; // shared occurence + WordOccurrence occurrence = null; // shared occurrence while (myLexer.getTokenType() != null) { final IElementType type = myLexer.getTokenType(); if (myIdentifierTokenSet.contains(type)) { - if (occurence == null) { - occurence = new WordOccurrence(fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE); + if (occurrence == null) { + occurrence = new WordOccurrence(fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE); } else { - occurence.init(fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE); + occurrence.init(fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE); } - if (!processor.process(occurence)) return; + if (!processor.process(occurrence)) return; } else if (myCommentTokenSet.contains(type)) { - if (!stripWords(processor, fileText,myLexer.getTokenStart(),myLexer.getTokenEnd(), WordOccurrence.Kind.COMMENTS,occurence, false)) return; + if (!stripWords(processor, fileText,myLexer.getTokenStart(),myLexer.getTokenEnd(), WordOccurrence.Kind.COMMENTS,occurrence, false)) return; } else if (myLiteralTokenSet.contains(type)) { - if (!stripWords(processor, fileText, myLexer.getTokenStart(),myLexer.getTokenEnd(),WordOccurrence.Kind.LITERALS,occurence, myMayHaveFileRefsInLiterals)) return; + if (!stripWords(processor, fileText, myLexer.getTokenStart(),myLexer.getTokenEnd(),WordOccurrence.Kind.LITERALS,occurrence, myMayHaveFileRefsInLiterals)) return; } else { // process all word-like characters as words // Plugin writers may have (Maximka in JavaScript especially) some keyword token types omitted from the identifierTokenSet - if (!stripWords(processor, fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE, occurence, false)) return; + if (!stripWords(processor, fileText, myLexer.getTokenStart(), myLexer.getTokenEnd(), WordOccurrence.Kind.CODE, occurrence, false)) return; } myLexer.advance(); } 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 f4f219c30e12..dbe5d13a980b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java @@ -268,10 +268,14 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr myScrollPane.setViewportBorder(JBScrollPane.createIndentBorder()); final DefaultActionGroup actions = new DefaultActionGroup(); - actions.add(new BackAction()); - actions.add(new ForwardAction()); + final BackAction back = new BackAction(); + final ForwardAction forward = new ForwardAction(); + actions.add(back); + actions.add(forward); actions.add(myExternalDocAction = new ExternalDocAction()); - + back.registerCustomShortcutSet(CustomShortcutSet.fromString("LEFT"), this); + forward.registerCustomShortcutSet(CustomShortcutSet.fromString("RIGHT"), this); + myExternalDocAction.registerCustomShortcutSet(CustomShortcutSet.fromString("UP"), this); if (additionalActions != null) { for (final AnAction action : additionalActions) { actions.add(action); diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java index c78850c2ca40..d2672ff88fc1 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java @@ -35,6 +35,7 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; +import com.intellij.ui.docking.DockManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -76,7 +77,7 @@ public class ExecutionManagerImpl extends ExecutionManager implements ProjectCom public RunContentManager getContentManager() { if (myContentManager == null) { - myContentManager = new RunContentManagerImpl(myProject); + myContentManager = new RunContentManagerImpl(myProject, DockManager.getInstance(myProject)); Disposer.register(myProject, myContentManager); } return myContentManager; diff --git a/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java index 7a42e42487ac..0a6f6b508feb 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java @@ -23,6 +23,7 @@ import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.GenericProgramRunner; +import com.intellij.execution.ui.layout.impl.DockableGridContainerFactory; import com.intellij.ide.DataManager; import com.intellij.ide.impl.ContentManagerWatcher; import com.intellij.openapi.Disposable; @@ -47,6 +48,7 @@ import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; import com.intellij.ui.content.*; +import com.intellij.ui.docking.DockManager; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; @@ -65,13 +67,17 @@ public class RunContentManagerImpl implements RunContentManager, Disposable { private static final Key DESCRIPTOR_KEY = new Key("Descriptor"); private final Project myProject; + private DockableGridContainerFactory myContentFactory; private final Map myToolwindowIdToContentManagerMap = new HashMap(); private final Map myListeners = new HashMap(); private final LinkedList myToolwindowIdZbuffer = new LinkedList(); - public RunContentManagerImpl(Project project) { + public RunContentManagerImpl(Project project, DockManager dockManager) { myProject = project; + myContentFactory = new DockableGridContainerFactory(); + dockManager.register(DockableGridContainerFactory.TYPE, myContentFactory); + Disposer.register(myProject, myContentFactory); } public void init() { diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/AttachCellAction.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/AttachCellAction.java deleted file mode 100644 index 062347e926d7..000000000000 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/AttachCellAction.java +++ /dev/null @@ -1,51 +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.execution.ui.layout.actions; - -import com.intellij.execution.ui.actions.BaseViewAction; -import com.intellij.execution.ui.layout.Grid; -import com.intellij.execution.ui.layout.GridCell; -import com.intellij.execution.ui.layout.ViewContext; -import com.intellij.idea.ActionsBundle; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.ui.content.Content; - -public class AttachCellAction extends BaseViewAction { - - protected void update(final AnActionEvent e, final ViewContext context, final Content[] content) { - if (content.length == 0 || !isDetached(context, content[0])) { - setEnabled(e, false); - return; - } - - Grid grid = context.findGridFor(content[0]); - - GridCell cell = grid.getCellFor(content[0]); - if (ViewContext.CELL_TOOLBAR_PLACE.equals(e.getPlace()) && content.length == 1) { - setEnabled(e, cell.getContentCount() == 1); - } else { - setEnabled(e, true); - if (cell.getContentCount() > 1) { - e.getPresentation().setText(ActionsBundle.message("action.Runner.AttachCells.text", cell.getContentCount())); - } - } - } - - protected void actionPerformed(final AnActionEvent e, final ViewContext context, final Content[] content) { - context.findCellFor(content[0]).attach(); - } -} diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/CloseViewAction.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/CloseViewAction.java index 98bfe91eecfe..cb18ab9d5840 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/CloseViewAction.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/CloseViewAction.java @@ -19,11 +19,19 @@ package com.intellij.execution.ui.layout.actions; import com.intellij.execution.ui.actions.BaseViewAction; import com.intellij.execution.ui.layout.ViewContext; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.util.IconLoader; import com.intellij.ui.content.Content; +import javax.swing.*; + public class CloseViewAction extends BaseViewAction { + private static final Icon ICON = IconLoader.getIcon("/actions/closeNew.png"); + private static final Icon HOVERED_ICON = IconLoader.getIcon("/actions/closeNewHovered.png"); + protected void update(final AnActionEvent e, final ViewContext context, final Content[] content) { setEnabled(e, isEnabled(context, content, e.getPlace())); + e.getPresentation().setIcon(ICON); + e.getPresentation().setHoveredIcon(HOVERED_ICON); } protected void actionPerformed(final AnActionEvent e, final ViewContext context, final Content[] content) { diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/DetachCellAction.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/DetachCellAction.java deleted file mode 100644 index 31215923d4db..000000000000 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/DetachCellAction.java +++ /dev/null @@ -1,59 +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.execution.ui.layout.actions; - -import com.intellij.execution.ui.actions.BaseViewAction; -import com.intellij.execution.ui.layout.Grid; -import com.intellij.execution.ui.layout.GridCell; -import com.intellij.execution.ui.layout.ViewContext; -import com.intellij.idea.ActionsBundle; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.ui.content.Content; - -public class DetachCellAction extends BaseViewAction { - protected void update(final AnActionEvent e, final ViewContext context, final Content[] content) { - if (content.length == 0 || isDetached(context, content[0])) { - setEnabled(e, false); - return; - } - - Grid grid = context.findGridFor(content[0]); - if (grid == null) { - setEnabled(e, false); - return; - } - - if (ViewContext.TAB_TOOLBAR_PLACE.equals(e.getPlace()) || (ViewContext.TAB_POPUP_PLACE.equals(e.getPlace()))) { - setEnabled(e, grid.getContents().size() == 1); - } - else { - GridCell cell = grid.getCellFor(content[0]); - if (ViewContext.CELL_TOOLBAR_PLACE.equals(e.getPlace()) && content.length == 1) { - setEnabled(e, cell.getContentCount() == 1); - } else { - setEnabled(e, true); - if (cell.getContentCount() > 1) { - e.getPresentation().setText(ActionsBundle.message("action.Runner.DetachCells.text", cell.getContentCount())); - } - } - } - } - - protected void actionPerformed(final AnActionEvent e, final ViewContext context, final Content[] content) { - context.findCellFor(content[0]).detach(); - } -} diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MinimizeViewAction.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MinimizeViewAction.java index 79982fb99f8b..fc16369ed688 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MinimizeViewAction.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MinimizeViewAction.java @@ -39,19 +39,15 @@ public class MinimizeViewAction extends BaseViewAction { return false; } - if (isDetached(context, content[0])) { - return false; - } - if (ViewContext.TAB_TOOLBAR_PLACE.equals(place) || ViewContext.TAB_POPUP_PLACE.equals(place)) { - return false; - } - else { Tab tab = getTabFor(context, content); if (tab == null) { return false; } - return tab.isDefault(); + return !tab.isDefault(); + } + else { + return getTabFor(context, content) != null; } } } diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MoveToGridAction.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MoveToGridAction.java deleted file mode 100644 index f66f5014d878..000000000000 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MoveToGridAction.java +++ /dev/null @@ -1,50 +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.execution.ui.layout.actions; - -import com.intellij.execution.ui.actions.BaseViewAction; -import com.intellij.execution.ui.layout.Grid; -import com.intellij.execution.ui.layout.Tab; -import com.intellij.execution.ui.layout.ViewContext; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.ui.content.Content; - -public class MoveToGridAction extends BaseViewAction { - protected void update(final AnActionEvent e, final ViewContext context, final Content[] content) { - if (!context.isMoveToGridActionEnabled() || content.length != 1) { - setEnabled(e, false); - return; - } - - if (isDetached(context, content[0])) { - setEnabled(e, false); - return; - } - - Grid grid = context.findGridFor(content[0]); - if (grid == null) { - setEnabled(e, false); - return; - } - Tab tab = context.getTabFor(grid); - setEnabled(e, tab != null && !tab.isDefault() && grid.getContents().size() == 1); - } - - protected void actionPerformed(final AnActionEvent e, final ViewContext context, final Content[] content) { - context.getCellTransform().moveToGrid(content[0]); - } -} diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MoveToTabAction.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MoveToTabAction.java deleted file mode 100644 index bf23cb10c3f0..000000000000 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/MoveToTabAction.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.execution.ui.layout.actions; - -import com.intellij.execution.ui.actions.BaseViewAction; -import com.intellij.execution.ui.layout.Grid; -import com.intellij.execution.ui.layout.Tab; -import com.intellij.execution.ui.layout.ViewContext; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.ui.content.Content; - -public class MoveToTabAction extends BaseViewAction { - protected void update(final AnActionEvent e, final ViewContext context, final Content[] content) { - if (!context.isMoveToGridActionEnabled() || content.length != 1) { - setEnabled(e, false); - return; - } - if (isDetached(context, content[0])) { - setEnabled(e, false); - return; - } - - Grid grid = context.findGridFor(content[0]); - if (grid == null) { - setEnabled(e, false); - return; - } - - - Tab tab = context.getTabFor(grid); - - if (ViewContext.TAB_TOOLBAR_PLACE.equals(e.getPlace())) { - setEnabled(e, false); - } else { - setEnabled(e, tab != null && tab.isDefault()); - } - } - - protected void actionPerformed(final AnActionEvent e, final ViewContext context, final Content[] content) { - context.getCellTransform().moveToTab(content[0]); - } -} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/AbstractTab.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/AbstractTab.java index 282ce022dc1a..fb2f1293e2a9 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/AbstractTab.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/AbstractTab.java @@ -21,6 +21,7 @@ import javax.swing.*; abstract class AbstractTab { int myIndex; + int myDefaultIndex = -1; String myDisplayName; Icon myIcon; @@ -38,6 +39,7 @@ abstract class AbstractTab { void copyFrom(final AbstractTab from) { myIndex = from.myIndex; + myDefaultIndex = from.myDefaultIndex; myDisplayName = from.myDisplayName; myIcon = from.myIcon; diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/DockableGridContainerFactory.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/DockableGridContainerFactory.java new file mode 100644 index 000000000000..4e9c694291d9 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/DockableGridContainerFactory.java @@ -0,0 +1,36 @@ +/* + * 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.execution.ui.layout.impl; + +import com.intellij.ui.docking.DockContainer; +import com.intellij.ui.docking.DockContainerFactory; +import com.intellij.ui.docking.DockableContent; + +/** + * @author Dennis.Ushakov + */ +public class DockableGridContainerFactory implements DockContainerFactory { + public static final String TYPE = "runner-grid"; + + @Override + public DockContainer createContainer(DockableContent content) { + final RunnerContentUi.DockableGrid dockableGrid = (RunnerContentUi.DockableGrid)content; + return new RunnerContentUi(dockableGrid.getRunnerUi(), dockableGrid.getOriginalRunnerUi(), dockableGrid.getWindow()); + } + + @Override + public void dispose() {} +} diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridCellImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridCellImpl.java index f479bc7860e9..53df6f2e8ea8 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridCellImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridCellImpl.java @@ -18,18 +18,16 @@ package com.intellij.execution.ui.layout.impl; import com.intellij.execution.ui.layout.*; import com.intellij.execution.ui.layout.actions.MinimizeViewAction; -import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.DataProvider; -import com.intellij.openapi.ui.popup.ComponentPopupBuilder; import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.*; -import com.intellij.openapi.wm.IdeFrame; -import com.intellij.openapi.wm.WindowManager; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.DimensionService; +import com.intellij.openapi.util.MutualMap; import com.intellij.ui.components.panels.NonOpaquePanel; -import com.intellij.ui.components.panels.Wrapper; import com.intellij.ui.content.Content; +import com.intellij.ui.docking.DockContainer; +import com.intellij.ui.docking.DockManager; import com.intellij.ui.switcher.SwitchTarget; import com.intellij.ui.tabs.JBTabs; import com.intellij.ui.tabs.TabInfo; @@ -43,15 +41,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; +import java.util.List; import java.util.Set; -public class GridCellImpl implements GridCell, Disposable { - +public class GridCellImpl implements GridCell { private final GridImpl myContainer; private final MutualMap myContents = new MutualMap(true); @@ -62,16 +59,12 @@ public class GridCellImpl implements GridCell, Disposable { private final PlaceInGrid myPlaceInGrid; private final ViewContextEx myContext; - private CellTransform.Restore.List myRestoreFromDetach; private JBPopup myPopup; - private boolean myDisposed; public GridCellImpl(ViewContextEx context, @NotNull GridImpl container, GridImpl.Placeholder placeholder, PlaceInGrid placeInGrid) { myContext = context; myContainer = container; - Disposer.register(container, this); - myPlaceInGrid = placeInGrid; myPlaceholder = placeholder; myTabs = new JBTabsImpl(myContext.getProject(), myContext.getActionManager(), myContext.getFocusManager(), container).setDataProvider(new DataProvider() { @@ -97,23 +90,17 @@ public class GridCellImpl implements GridCell, Disposable { } }).setSideComponentVertical(!context.getLayoutSettings().isToolbarHorizontal()) .setStealthTabMode(true) - .setFocusCycle(false).setPaintFocus(true).setProvideSwitchTargets(false); + .setFocusCycle(false).setPaintFocus(true).setProvideSwitchTargets(false).setTabDraggingEnabled(true); myTabs.addTabMouseListener(new MouseAdapter() { public void mousePressed(final MouseEvent e) { if (UIUtil.isCloseClick(e)) { - if (isDetached()) { - myPopup.cancel(); - myPopup = null; - } - else { minimize(e); - } } } }); rebuildPopupGroup(); - myTabs.addListener(new TabsListener() { + myTabs.addListener(new TabsListener.Adapter() { public void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection) { if (oldSelection != null && myContext.isStateBeingRestored()) { @@ -181,7 +168,7 @@ public class GridCellImpl implements GridCell, Disposable { } } else { - if (myPlaceholder.isNull() && !isDetached()) { + if (myPlaceholder.isNull()) { myPlaceholder.setContent(myTabs.getComponent()); } @@ -211,7 +198,7 @@ public class GridCellImpl implements GridCell, Disposable { ActionGroup group = (ActionGroup)myContext.getActionManager().getAction(RunnerContentUi.VIEW_TOOLBAR); tabInfo.setTabLabelActions(group, ViewContext.CELL_TOOLBAR_PLACE); - + tabInfo.setDragOutDelegate(((RunnerContentUi)myContext).myDragOutDelegate); return tabInfo; } @@ -248,7 +235,7 @@ public class GridCellImpl implements GridCell, Disposable { return myMinimizedContents.contains(content); } - public java.util.List getTargets(boolean onlyVisible) { + public List getTargets(boolean onlyVisible) { if (myTabs.getPresentation().isHideTabs()) return new ArrayList(); return myTabs.getTargets(onlyVisible, false); @@ -287,7 +274,7 @@ public class GridCellImpl implements GridCell, Disposable { } @Nullable - private TabInfo getTabFor(Content content) { + TabInfo getTabFor(Content content) { return myContents.getValue(content); } @@ -306,14 +293,21 @@ public class GridCellImpl implements GridCell, Disposable { restoreProportions(); Content[] contents = getContents(); + int window = 0; for (Content each : contents) { - if (myContainer.getStateFor(each).isMinimizedInGrid()) { + final View view = myContainer.getStateFor(each); + if (view.isMinimizedInGrid()) { minimize(each); } + window = view.getWindow(); } - - if (!isRestoringFromDetach() && myContainer.getTab().isDetached(myPlaceInGrid) && contents.length > 0) { - _detach(!myContext.isStateBeingRestored()).notifyWhenDone(result); + final Tab tab = myContainer.getTab(); + final boolean detached = (tab != null && tab.isDetached(myPlaceInGrid)) || window != myContext.getWindow(); + if (detached && contents.length > 0) { + if (tab != null) { + tab.setDetached(myPlaceInGrid, false); + } + myContext.detachTo(window, this).notifyWhenDone(result); } else { result.setDone(); } @@ -321,7 +315,7 @@ public class GridCellImpl implements GridCell, Disposable { return result; } - private Content[] getContents() { + Content[] getContents() { return myContents.getKeys().toArray(new Content[myContents.size()]); } @@ -339,6 +333,14 @@ public class GridCellImpl implements GridCell, Disposable { for (Content each : myMinimizedContents) { saveState(each, true); } + + final DimensionService service = DimensionService.getInstance(); + final Dimension size = myContext.getContentManager().getComponent().getSize(); + service.setSize(getDimensionKey(), size, myContext.getProject()); + if (myContext.getWindow() != 0) { + final JFrame frame = (JFrame)DockManager.getInstance(myContext.getProject()).getIdeFrame((DockContainer)myContext); + service.setLocation(getDimensionKey(), frame.getLocationOnScreen()); + } } public void saveProportions() { @@ -350,8 +352,7 @@ public class GridCellImpl implements GridCell, Disposable { state.setMinimizedInGrid(minimized); state.setPlaceInGrid(myPlaceInGrid); state.assignTab(myContainer.getTabIndex()); - - state.getTab().setDetached(myPlaceInGrid, isDetached()); + state.setWindow(myContext.getWindow()); } public void restoreProportions() { @@ -362,7 +363,7 @@ public class GridCellImpl implements GridCell, Disposable { for (Content each : myContents.getKeys()) { final TabInfo eachTab = getTabFor(each); boolean isSelected = eachTab != null && myTabs.getSelectedInfo() == eachTab; - if (isSelected && (isShowing || isDetached())) { + if (isSelected && isShowing) { myContext.getContentManager().addSelectedContent(each); } else { @@ -391,147 +392,22 @@ public class GridCellImpl implements GridCell, Disposable { } } - public ActionCallback detach() { - return _detach(true); + @Nullable + public Point getLocation() { + return DimensionService.getInstance().getLocation(getDimensionKey(), myContext.getProject()); } - private ActionCallback _detach(final boolean requestFocus) { - myContext.saveUiState(); - - final DimensionService dimService = DimensionService.getInstance(); - Point storedLocation = dimService.getLocation(getDimensionKey(), myContext.getProject()); - Dimension storedSize = dimService.getSize(getDimensionKey(), myContext.getProject()); - - final IdeFrame frame = WindowManager.getInstance().getIdeFrame(myContext.getProject()); - final Rectangle targetBounds = frame.suggestChildFrameBounds(); - - - if (storedLocation != null && storedSize != null) { - targetBounds.setLocation(storedLocation); - targetBounds.setSize(storedSize); - } - - final ActionCallback result = new ActionCallback(); - - if (storedLocation == null || storedSize == null) { - if (myContents.size() > 0) { - myContext.validate(myContents.getKeys().iterator().next(), new ActiveRunnable() { - public ActionCallback run() { - if (!myTabs.getComponent().isShowing()) { - detachTo(targetBounds.getLocation(), targetBounds.getSize(), false, requestFocus).notifyWhenDone(result); - } else { - detachForShowingTabs(requestFocus).notifyWhenDone(result); - } - - return new ActionCallback.Done(); - } - }); - - return result; - } - } - - detachTo(targetBounds.getLocation(), targetBounds.getSize(), false, requestFocus).notifyWhenDone(result); - - return result; + @Nullable + public Dimension getSize() { + return DimensionService.getInstance().getSize(getDimensionKey(), myContext.getProject()); } - - private ActionCallback detachForShowingTabs(boolean requestFocus) { - return detachTo(myTabs.getComponent().getLocationOnScreen(), myTabs.getComponent().getSize(), false, requestFocus); - } - - private ActionCallback detachTo(Point screenPoint, Dimension size, boolean dragging, final boolean requestFocus) { - if (isDetached()) { - if (myPopup != null) { - return new ActionCallback.Done(); - } - } - - final Content[] contents = getContents(); - - myRestoreFromDetach = new CellTransform.Restore.List(); - - myRestoreFromDetach.add(myPlaceholder.detach()); - myRestoreFromDetach.add(myContainer.detach(contents)); - myRestoreFromDetach.add(new CellTransform.Restore() { - public ActionCallback restoreInGrid() { - ensureVisible(); - return new ActionCallback.Done(); - } - }); - - myPopup = createPopup(dragging, requestFocus); - myPopup.setSize(size); - myPopup.setLocation(screenPoint); - myPopup.show(myContext.getContentManager().getComponent()); - - myContext.saveUiState(); - - myTabs.updateTabActions(true); - - return new ActionCallback.Done(); - } - - private void ensureVisible() { - if (myTabs.getSelectedInfo() != null) { - myContext.select(getContentFor(myTabs.getSelectedInfo()), true); - } - } - - private JBPopup createPopup(boolean dragging, final boolean requestFocus) { - Wrapper wrapper = new Wrapper(myTabs.getComponent()); - wrapper.setBorder(new EmptyBorder(1, 0, 0, 0)); - final ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(wrapper, myTabs.getComponent()) - .setTitle(myContainer.getSessionName()) - .setMovable(true) - .setRequestFocus(requestFocus) - .setFocusable(true) - .setResizable(true) - .setDimensionServiceKey(myContext.getProject(), getDimensionKey(), true) - .setCancelOnOtherWindowOpen(false) - .setCancelOnClickOutside(false) - .setCancelKeyEnabled(true) - .setLocateByContent(dragging) - .setLocateWithinScreenBounds(!dragging) - .setCancelKeyEnabled(false) - .setBelongsToGlobalPopupStack(false) - .setModalContext(false) - .setCancelCallback(new Computable() { - public Boolean compute() { - if (myDisposed || myContents.size() == 0) return Boolean.TRUE; - myRestoreFromDetach.restoreInGrid(); - myRestoreFromDetach = null; - myContext.saveUiState(); - myTabs.updateTabActions(true); - return Boolean.TRUE; - } - }); - - return builder.createPopup(); - } - - public void attach() { - if (isDetached()) { - myPopup.cancel(); - myPopup = null; - } - } - - - public boolean isDetached() { - return myRestoreFromDetach != null && !myRestoreFromDetach.isRestoringNow(); - } - - public boolean isRestoringFromDetach() { - return myRestoreFromDetach != null && myRestoreFromDetach.isRestoringNow(); - } - + private String getDimensionKey() { return "GridCell.Tab." + myContainer.getTab().getIndex() + "." + myPlaceInGrid.name(); } - public boolean isValidForCalculatePropertions() { - return !isDetached() && getContentCount() > 0; + public boolean isValidForCalculateProportions() { + return getContentCount() > 0; } public void minimize(Content content) { @@ -553,13 +429,4 @@ public class GridCellImpl implements GridCell, Disposable { updateSelection(myTabs.getComponent().getRootPane() != null); return new ActionCallback.Done(); } - - public void dispose() { - myDisposed = true; - - if (myPopup != null) { - myPopup.cancel(); - myPopup = null; - } - } } diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridImpl.java index dfb6659f6c1a..f023d86d3a9b 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridImpl.java @@ -35,7 +35,7 @@ import java.awt.*; import java.util.*; import java.util.List; -public class GridImpl extends Wrapper implements Grid, Disposable, CellTransform.Facade, DataProvider { +public class GridImpl extends Wrapper implements Grid, Disposable, DataProvider { private final ThreeComponentsSplitter myTopSplit = new ThreeComponentsSplitter(); private final Splitter mySplitter = new Splitter(true); @@ -152,8 +152,7 @@ public class GridImpl extends Wrapper implements Grid, Disposable, CellTransform public boolean updateGridUI() { for (final GridCellImpl cell : myPlaceInGrid2Cell.values()) { - final boolean eachToHide = myContents.size() == 1 && !cell.isDetached(); - cell.setHideTabs(eachToHide); + cell.setHideTabs(myContents.size() == 1); } final Content onlyContent = myContents.get(0); @@ -252,7 +251,7 @@ public class GridImpl extends Wrapper implements Grid, Disposable, CellTransform final GridCellImpl cell = myPlaceInGrid2Cell.get(placeInGrid); - if (!cell.isValidForCalculatePropertions()) return; + if (!cell.isValidForCalculateProportions()) return; final TabImpl tab = (TabImpl)getTab(); @@ -335,19 +334,12 @@ public class GridImpl extends Wrapper implements Grid, Disposable, CellTransform ArrayList result = new ArrayList(); for (Content each : getContents()) { - if (!isDetached(each)) { - result.add(each); - } + result.add(each); } return result; } - - public boolean isDetached(Content content) { - return getCellFor(content).isDetached(); - } - public List getContents() { return myContents; } @@ -360,28 +352,6 @@ public class GridImpl extends Wrapper implements Grid, Disposable, CellTransform }); } - public void moveToTab(final Content content) { - myViewContext.getCellTransform().moveToTab(content); - } - - public void moveToGrid(final Content content) { - myViewContext.getCellTransform().moveToGrid(content); - } - - public CellTransform.Restore detach(final Content[] content) { - final CellTransform.Restore.List restore = new CellTransform.Restore.List(); - restore.add(myViewContext.getCellTransform().detach(content)); - restore.add(new CellTransform.Restore() { - public ActionCallback restoreInGrid() { - revalidate(); - repaint(); - return new ActionCallback.Done(); - } - }); - - return restore; - } - @Nullable public Object getData(@NonNls final String dataId) { if (ViewContext.CONTEXT_KEY.is(dataId)) { @@ -394,10 +364,6 @@ public class GridImpl extends Wrapper implements Grid, Disposable, CellTransform return null; } - public String getSessionName() { - return mySessionName; - } - @Nullable public SwitchTarget getCellFor(Component c) { Component eachParent = c; @@ -419,9 +385,7 @@ public class GridImpl extends Wrapper implements Grid, Disposable, CellTransform Collection cells = myPlaceInGrid2Cell.values(); ArrayList result = new ArrayList(); for (GridCellImpl each : cells) { - if (!each.isDetached()) { - result.addAll(each.getTargets(onlyVisible)); - } + result.addAll(each.getTargets(onlyVisible)); } return result; } diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/JBRunnerTabs.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/JBRunnerTabs.java new file mode 100644 index 000000000000..944876b1cca3 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/JBRunnerTabs.java @@ -0,0 +1,276 @@ +/* + * 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.execution.ui.layout.impl; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.ui.Gray; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.tabs.TabInfo; +import com.intellij.ui.tabs.TabsUtil; +import com.intellij.ui.tabs.UiDecorator; +import com.intellij.ui.tabs.impl.JBTabsImpl; +import com.intellij.ui.tabs.impl.TabLabel; +import com.intellij.util.ui.SameColor; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.util.List; +import java.util.Map; + +/** + * @author Dennis.Ushakov + */ +public class + JBRunnerTabs extends JBTabsImpl { + public JBRunnerTabs(@Nullable Project project, ActionManager actionManager, IdeFocusManager focusManager, @NotNull Disposable parent) { + super(project, actionManager, focusManager, parent); + } + + @Override + protected void paintFirstGhost(Graphics2D g2d) {} + + @Override + protected void paintLastGhost(Graphics2D g2d) {} + + public boolean isGhostsAlwaysVisible() { + return false; + } + + protected void doPaintInactive(Graphics2D g2d, + boolean leftGhostExists, + TabLabel label, + Rectangle effectiveBounds, + boolean rightGhostExists) { + Insets insets = getTabsBorder().getEffectiveBorder(); + + int _x = effectiveBounds.x + insets.left; + int _y = effectiveBounds.y + insets.top + 5; + int _width = effectiveBounds.width - insets.left - insets.right; + int _height = effectiveBounds.height - insets.top - insets.bottom - 5; + _height -= TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT; + + g2d + .setPaint(new GradientPaint(_x, _y, new Color(255, 255, 255, 180), _x, _y + effectiveBounds.height, new Color(255, 255, 255, 100))); + g2d.fillRect(_x, _y, _width, _height); + + g2d.setColor(new Color(255, 255, 255, 100)); + g2d.drawRect(_x, _y, _width - 1, _height - 1); + } + + @Override + protected void doPaintBackground(Graphics2D g2d, Rectangle clip) { + g2d.setColor(UIUtil.getPanelBackground()); + g2d.fill(clip); + + g2d.setColor(new Color(0, 0, 0, 50)); + g2d.fill(clip); + + List visibleInfos = getVisibleInfos(); + + Insets insets = getTabsBorder().getEffectiveBorder(); + + int maxOffset = 0; + int maxLength = 0; + + for (int i = visibleInfos.size() - 1; i >= 0; i--) { + TabInfo visibleInfo = visibleInfos.get(i); + TabLabel tabLabel = myInfo2Label.get(visibleInfo); + Rectangle r = tabLabel.getBounds(); + if (r.width == 0 || r.height == 0) continue; + maxOffset = r.x + r.width; + maxLength = r.height; + break; + } + + maxOffset++; + + Rectangle r2 = getBounds(); + + Rectangle rectangle; + int y = r2.y + insets.top; + int height = maxLength - insets.top - insets.bottom; + height -= TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT; + + rectangle = new Rectangle(maxOffset, y, r2.width - maxOffset - insets.left - insets.right, height); + + g2d.setPaint(UIUtil.getPanelBackground()); + g2d.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + g2d.fillRect(0, 0, rectangle.x + rectangle.width, 5); + g2d.fillRect(2, maxLength, getSize().width, getSize().height); + g2d.drawLine(0, 0, 0, getSize().height); + } + + protected void paintSelectionAndBorder(Graphics2D g2d) { + if (getSelectedInfo() == null) return; + + TabLabel label = getSelectedLabel(); + Rectangle r = label.getBounds(); + r = new Rectangle(r.x, r.y + 5, r.width, r.height - 5); + + ShapeInfo selectedShape = _computeSelectedLabelShape(r); + + Insets insets = getTabsBorder().getEffectiveBorder(); + Insets i = selectedShape.path.transformInsets(insets); + + int _x = r.x; + int _y = r.y; + int _height = r.height; + + if (!isHideTabs()) { + g2d.setPaint(new GradientPaint(_x, _y, new SameColor(255), _x, _y + _height, UIUtil.getPanelBackground())); + + g2d.fill(selectedShape.fillPath.getShape()); + + g2d.setColor(new Color(255, 255, 255, 180)); + g2d.draw(selectedShape.fillPath.getShape()); + } + g2d.setColor(UIUtil.getPanelBackground()); + g2d.fillRect(2, selectedShape.labelPath.getMaxY() - 3, selectedShape.path.getMaxX() - 3, 4); + g2d.drawLine(1, selectedShape.labelPath.getMaxY(), 1, getHeight() - 1); + g2d.drawLine(selectedShape.path.getMaxX() - 1, selectedShape.labelPath.getMaxY() - 4, + selectedShape.path.getMaxX() - 1, getHeight() - 1); + + if (isHideTabs()) return; + g2d.setColor(new Color(0, 0, 0, 50)); + g2d.drawLine(1, selectedShape.labelPath.getMaxY(), 1, getHeight() - 1); + g2d.drawLine(selectedShape.path.getMaxX() - 1, selectedShape.labelPath.getMaxY() - 4, + selectedShape.path.getMaxX() - 1, getHeight() - 1); + } + + @Override + public Color getBackground() { + return Gray._142; + } + + protected ShapeInfo _computeSelectedLabelShape(Rectangle r) { + final ShapeInfo shape = new ShapeInfo(); + + shape.path = getEffectiveLayout().createShapeTransform(getSize()); + shape.insets = shape.path.transformInsets(getLayoutInsets()); + shape.labelPath = shape.path.createTransform(r); + + shape.labelBottomY = shape.labelPath.getMaxY() - shape.labelPath.deltaY(TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT - 1); + shape.labelTopY = shape.labelPath.getY() + shape.labelPath.deltaY(1); + shape.labelLeftX = shape.labelPath.getX(); + shape.labelRightX = shape.labelPath.getMaxX() - shape.labelPath.deltaX(1); + + int leftX = shape.insets.left; + + shape.path.moveTo(leftX, shape.labelBottomY); + shape.path.lineTo(shape.labelLeftX, shape.labelBottomY); + shape.path.lineTo(shape.labelLeftX, shape.labelTopY); + shape.path.lineTo(shape.labelRightX, shape.labelTopY); + shape.path.lineTo(shape.labelRightX, shape.labelBottomY); + + int lastX = shape.path.getWidth() - shape.path.deltaX(shape.insets.right); + + shape.path.lineTo(lastX, shape.labelBottomY); + shape.path.lineTo(lastX, shape.labelBottomY + shape.labelPath.deltaY(TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT - 1)); + shape.path.lineTo(leftX, shape.labelBottomY + shape.labelPath.deltaY(TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT - 1)); + + shape.path.closePath(); + shape.fillPath = shape.path.copy(); + + return shape; + } + + @Override + public int getToolbarInset() { + return 8; + } + + public boolean shouldAddToGlobal(Point point) { + final TabLabel label = getSelectedLabel(); + if (label == null || point == null) { + return true; + } + final Rectangle bounds = label.getBounds(); + return point.y <= bounds.y + bounds.height; + } + + @Override + public Rectangle layout(JComponent c, Rectangle bounds) { + if (c instanceof Toolbar) { + bounds.height -= 5; + return super.layout(c, bounds); + } + if (c instanceof GridImpl) { + bounds.x -= 1; + bounds.width += 1; + } + return super.layout(c, bounds); + } + + @Override + public void processDropOver(TabInfo over, RelativePoint relativePoint) { + super.processDropOver(over, relativePoint); + final Point point = relativePoint.getPoint(getComponent()); + for (Map.Entry entry : myInfo2Label.entrySet()) { + final TabLabel label = entry.getValue(); + if (label.getBounds().contains(point) && myDropInfo != entry.getKey()) { + select(entry.getKey(), false); + break; + } + } + } + + @Override + protected TabLabel createTabLabel(TabInfo info) { + return new MyTabLabel(this, info); + } + + private static class MyTabLabel extends TabLabel { + public MyTabLabel(JBTabsImpl tabs, final TabInfo info) { + super(tabs, info); + } + + @Override + public void apply(UiDecorator.UiDecoration decoration) { + setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL)); + myLabel.setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL)); + setBorder(new EmptyBorder(7, 5, 7, 5)); + } + + @Override + public void setTabActionsAutoHide(boolean autoHide) { + super.setTabActionsAutoHide(autoHide); + apply(null); + } + + @Override + public void setTabActions(ActionGroup group) { + super.setTabActions(group); + if (myActionPanel != null) { + final JComponent wrapper = (JComponent)myActionPanel.getComponent(0); + wrapper.remove(0); + wrapper.add(Box.createHorizontalStrut(6), BorderLayout.WEST); + } + } + + @Override + protected int getSelectedOffset() { + return getNonSelectedOffset(); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java index b2732529f4f6..4448dacb739e 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerContentUi.java @@ -28,21 +28,28 @@ import com.intellij.openapi.util.ActiveRunnable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Ref; import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.ToolWindow; import com.intellij.ui.UIBundle; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.ui.awt.RelativeRectangle; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.ui.components.panels.Wrapper; import com.intellij.ui.content.*; +import com.intellij.ui.docking.DockContainer; +import com.intellij.ui.docking.DockManager; +import com.intellij.ui.docking.DockableContent; +import com.intellij.ui.docking.DragSession; +import com.intellij.ui.docking.impl.DockManagerImpl; import com.intellij.ui.switcher.QuickActionProvider; import com.intellij.ui.switcher.SwitchProvider; import com.intellij.ui.switcher.SwitchTarget; import com.intellij.ui.tabs.JBTabs; import com.intellij.ui.tabs.TabInfo; import com.intellij.ui.tabs.TabsListener; -import com.intellij.ui.tabs.UiDecorator; -import com.intellij.ui.tabs.impl.JBTabsImpl; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.AbstractLayoutManager; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,13 +57,15 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; import java.util.List; +import java.util.concurrent.CopyOnWriteArraySet; public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Facade, ViewContextEx, PropertyChangeListener, SwitchProvider, - QuickActionProvider { + QuickActionProvider, DockContainer { @NonNls public static final String LAYOUT = "Runner.Layout"; @NonNls public static final String VIEW_POPUP = "Runner.View.Popup"; @@ -70,8 +79,9 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac MyComponent myComponent = new MyComponent(); private final Wrapper myToolbar = new Wrapper(); + final MyDragOutDelegate myDragOutDelegate = new MyDragOutDelegate(); - JBTabs myTabs; + JBRunnerTabs myTabs; private final Comparator myTabsComparator = new Comparator() { public int compare(final TabInfo o1, final TabInfo o2) { //noinspection ConstantConditions @@ -110,6 +120,20 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac private int myAttractionCount; private ActionGroup myLeftToolbarActions; + private JBTabs myCurrentOver; + private Image myCurrentOverImg; + private TabInfo myCurrentOverInfo; + private RunnerContentUi myOriginal; + private CopyOnWriteArraySet myDockingListeners = new CopyOnWriteArraySet(); + private Set myChildren = new TreeSet(new Comparator() { + @Override + public int compare(RunnerContentUi o1, RunnerContentUi o2) { + return o1.myWindow - o2.myWindow; + } + }); + private int myWindow; + private boolean myDisposing; + public RunnerContentUi(Project project, RunnerLayoutUi ui, ActionManager actionManager, @@ -124,6 +148,12 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac myFocusManager = focusManager; } + public RunnerContentUi(RunnerContentUi ui, RunnerContentUi original, int window) { + this(ui.myProject, ui.myRunnerUi, ui.myActionManager, ui.myFocusManager, ui.myLayoutSettings, ui.mySessionName); + myOriginal = original; + original.myChildren.add(this); + myWindow = window == 0 ? original.findFreeWindow() : window; + } public void setTopActions(@NotNull final ActionGroup topActions, @NotNull String place) { myTopActions = topActions; @@ -150,32 +180,27 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac public void initUi() { if (myTabs != null) return; - myTabs = new JBTabsImpl(myProject, myActionManager, myFocusManager, this) - .setDataProvider(new DataProvider() { - public Object getData(@NonNls final String dataId) { - if (ViewContext.CONTENT_KEY.is(dataId)) { - TabInfo info = myTabs.getTargetInfo(); - if (info != null) { - return getGridFor(info).getData(dataId); - } + myTabs = (JBRunnerTabs)new JBRunnerTabs(myProject, myActionManager, myFocusManager, this).setDataProvider(new DataProvider() { + public Object getData(@NonNls final String dataId) { + if (ViewContext.CONTENT_KEY.is(dataId)) { + TabInfo info = myTabs.getTargetInfo(); + if (info != null) { + return getGridFor(info).getData(dataId); } - else if (ViewContext.CONTEXT_KEY.is(dataId)) { - return RunnerContentUi.this; - } - return null; } - }).setProvideSwitchTargets(false).setInnerInsets(new Insets(1, 0, 0, 0)).setToDrawBorderIfTabsHidden(false) - .setUiDecorator(new UiDecorator() { - @NotNull - public UiDecoration getDecoration() { - return new UiDecoration(null, new Insets(1, 8, 1, 8)); + else if (ViewContext.CONTEXT_KEY.is(dataId)) { + return RunnerContentUi.this; } - }).getJBTabs(); + return null; + } + }).setTabLabelActionsAutoHide(false).setProvideSwitchTargets(false).setInnerInsets(new Insets(0, 0, 0, 0)) + .setToDrawBorderIfTabsHidden(false).setTabDraggingEnabled(isMoveToGridActionEnabled()).setUiDecorator(null).getJBTabs(); rebuildTabPopup(); - - myTabs.getPresentation().setPaintBorder(0, 0, 0, 0).setTabSidePaintBorder(2).setPaintFocus(false) + myTabs.getPresentation().setPaintBorder(0, 0, 0, 0).setPaintFocus(false) .setRequestFocusOnLastFocusedComponent(true); + myTabs.getComponent().setBackground(myToolbar.getBackground()); + myTabs.getComponent().setBorder(new EmptyBorder(0, 2, 0, 0)); final NonOpaquePanel wrappper = new NonOpaquePanel(new BorderLayout(0, 0)); wrappper.add(myToolbar, BorderLayout.WEST); @@ -194,6 +219,11 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac } } + @Override + public void tabsMoved() { + saveUiState(); + } + public void selectionChanged(final TabInfo oldSelection, final TabInfo newSelection) { if (!myTabs.getComponent().isShowing()) return; @@ -207,6 +237,14 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac } } }); + + if (myOriginal != null) { + final ContentManager manager = ContentFactory.SERVICE.getInstance().createContentManager(this, false, myProject); + Disposer.register((Disposable)myRunnerUi, manager); + manager.getComponent(); + } else { + DockManager.getInstance(myProject).register(this); + } } private void rebuildTabPopup() { @@ -249,10 +287,15 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac return group; } - public void doWhenInitialized(final Runnable runnable) { - myInitialized.doWhenDone(runnable); + @Override + public boolean isOriginal() { + return myOriginal == null; } + @Override + public int getWindow() { + return myWindow; + } public void propertyChange(final PropertyChangeEvent evt) { Content content = (Content)evt.getSource(); @@ -302,11 +345,193 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac } } + @Override + public ActionCallback detachTo(int window, GridCell cell) { + if (myOriginal != null) { + return myOriginal.detachTo(window, cell); + } + RunnerContentUi target = null; + if (window > 0) { + for (RunnerContentUi child : myChildren) { + if (child.myWindow == window) { + target = child; + break; + } + } + } + final GridCellImpl gridCell = (GridCellImpl)cell; + final Content[] contents = gridCell.getContents(); + storeDefaultIndices(contents); + for (Content content : contents) { + content.putUserData(RunnerLayout.DROP_INDEX, getStateFor(content).getTab().getIndex()); + } + Dimension size = gridCell.getSize(); + if (size == null) { + size = new Dimension(200, 200); + } + final DockableGrid content = new DockableGrid(null, null, size, Arrays.asList(contents), window); + if (target != null) { + target.add(content, null); + } else { + final Point location = gridCell.getLocation(); + location.translate(size.width / 2, size.height / 2); + getDockManager().createNewDockContainerFor(content, new RelativePoint(location)); + } + return new ActionCallback.Done(); + } + + private void storeDefaultIndices(Content[] contents) { + for (Content content : contents) { + content.putUserData(RunnerLayout.DEFAULT_INDEX, getStateFor(content).getTab().getDefaultIndex()); + } + } + + @Override + public RelativeRectangle getAcceptArea() { + return new RelativeRectangle(myTabs.getComponent()); + } + + @Override + public boolean canAccept(DockableContent content, RelativePoint point) { + if (!(content instanceof DockableGrid)) { + return false; + } + final RunnerContentUi ui = ((DockableGrid)content).getOriginalRunnerUi(); + return ui.getProject() == myProject && ui.mySessionName.equals(mySessionName); + } + + @Override public JComponent getComponent() { initUi(); return myComponent; } + @Override + public JComponent getContainerComponent() { + initUi(); + return myManager.getComponent(); + } + + @Override + public void add(DockableContent dockable, RelativePoint dropTarget) { + saveUiState(); + + final DockableGrid dockableGrid = (DockableGrid)dockable; + final List contents = dockableGrid.getContents(); + final boolean wasRestoring = myOriginal != null && myOriginal.isStateBeingRestored(); + setStateIsBeingRestored(true, this); + try { + final Point point = dropTarget != null ? dropTarget.getPoint(myComponent) : null; + boolean hadGrid = !myTabs.shouldAddToGlobal(point); + + for (Content content : contents) { + dockableGrid.getRunnerUi().myManager.removeContent(content, false); + myManager.removeContent(content, false); + if (hadGrid && contents.size() == 1 && !wasRestoring) { + getStateFor(content).assignTab(getTabFor(getSelectedGrid())); + getStateFor(content).setPlaceInGrid(myLayoutSettings.getDefaultGridPlace(content)); + } else if (contents.size() == 1 && !wasRestoring) { + getStateFor(content).assignTab(myLayoutSettings.createNewTab()); + getStateFor(content).setPlaceInGrid(myLayoutSettings.getDefaultGridPlace(content)); + } + getStateFor(content).setWindow(myWindow); + myManager.addContent(content); + } + } finally { + setStateIsBeingRestored(false, this); + } + + saveUiState(); + + updateTabsUI(true); + } + + @Override + public void closeAll() { + final Content[] contents = myManager.getContents(); + for (Content content : contents) { + getStateFor(content).setWindow(0); + } + myManager.removeAllContents(false); + for (Content content : contents) { + myOriginal.myManager.addContent(content); + myOriginal.findCellFor(content).minimize(content); + } + } + + @Override + public void addListener(final Listener listener, Disposable parent) { + myDockingListeners.add(listener); + Disposer.register(parent, new Disposable() { + @Override + public void dispose() { + myDockingListeners.remove(listener); + } + }); + } + + @Override + public boolean isEmpty() { + return myTabs.isEmptyVisible() || myDisposing; + } + + @Override + public Image startDropOver(DockableContent content, RelativePoint point) { + return null; + } + + @Override + public Image processDropOver(DockableContent content, RelativePoint point) { + JBTabs current = getTabsAt(content, point); + + if (myCurrentOver != null && myCurrentOver != current) { + resetDropOver(content); + } + + if (myCurrentOver == null && current != null) { + myCurrentOver = current; + Presentation presentation = content.getPresentation(); + myCurrentOverInfo = new TabInfo(new JLabel("")).setText(presentation.getText()).setIcon(presentation.getIcon()); + myCurrentOverImg = myCurrentOver.startDropOver(myCurrentOverInfo, point); + } + + if (myCurrentOver != null) { + myCurrentOver.processDropOver(myCurrentOverInfo, point); + } + + return myCurrentOverImg; + } + + @Nullable + private JBTabs getTabsAt(DockableContent content, RelativePoint point) { + if (content instanceof DockableGrid) { + final Point p = point.getPoint(getComponent()); + Component c = SwingUtilities.getDeepestComponentAt(getComponent(), p.x, p.y); + while (c != null) { + if (c instanceof JBTabs) { + return (JBTabs)c; + } + c = c.getParent(); + } + } + return null; + } + + @Override + public void resetDropOver(DockableContent content) { + if (myCurrentOver != null) { + myCurrentOver.resetDropOver(myCurrentOverInfo); + myCurrentOver = null; + myCurrentOverInfo = null; + myCurrentOverImg = null; + } + } + + @Override + public boolean isDisposeWhenEmpty() { + return myOriginal != null; + } + public boolean isCycleRoot() { return false; } @@ -337,6 +562,7 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac event.getContent().addPropertyChangeListener(RunnerContentUi.this); + fireContentOpened(event.getContent()); } public void contentRemoved(final ContentManagerEvent event) { @@ -349,6 +575,7 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac removeGridIfNeeded(grid); } updateTabsUI(false); + fireContentClosed(event.getContent()); } public void contentRemoveQuery(final ContentManagerEvent event) { @@ -387,6 +614,16 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac grid = new GridImpl(this, mySessionName); grid.setBorder(new EmptyBorder(1, 0, 0, 0)); + if (myCurrentOver != null || myOriginal != null) { + Integer forcedDropIndex = content.getUserData(RunnerLayout.DROP_INDEX); + final int index = myTabs.getDropInfoIndex() + (myOriginal != null ? myOriginal.getTabOffsetFor(this) : 0); + final TabImpl tab = myLayoutSettings.getOrCreateTab(-1); + final Integer defaultIndex = content.getUserData(RunnerLayout.DEFAULT_INDEX); + tab.setDefaultIndex(defaultIndex != null ? defaultIndex : -1); + tab.setIndex(forcedDropIndex != null ? forcedDropIndex : index); + getStateFor(content).assignTab(tab); + } + TabInfo tab = new TabInfo(grid).setObject(getStateFor(content).getTab()).setText("Tab"); @@ -418,6 +655,15 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac return grid; } + private int getTabOffsetFor(RunnerContentUi ui) { + int offset = myTabs.getTabCount(); + for (RunnerContentUi child : myChildren) { + if (child == ui) break; + offset += child.myTabs.getTabCount(); + } + return offset; + } + @Nullable public GridCell findCellFor(final Content content) { GridImpl cell = getGridFor(content, false); @@ -498,9 +744,7 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac for (TabInfo each : tabs) { hasToolbarContent |= updateTabUI(each); } - - myTabs.getPresentation().setHideTabs(!hasToolbarContent && tabs.size() <= 1); - + myTabs.getPresentation().setHideTabs(!hasToolbarContent && tabs.size() <= 1 && myOriginal == null); myTabs.updateTabActions(validateNow); if (validateNow) { @@ -508,7 +752,7 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac } } - private static boolean updateTabUI(TabInfo tab) { + private boolean updateTabUI(TabInfo tab) { TabImpl t = getTabFor(tab); if (t == null) { return false; @@ -534,9 +778,11 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac } if (icon == null && contents.size() == 1) { + tab.setHidden(grid.isMinimized(contents.get(0))); icon = contents.get(0).getIcon(); } + tab.setDragOutDelegate(myTabs.getTabs().size() > 1 || !isOriginal() ? myDragOutDelegate : null); Tab gridTab = grid.getTab(); tab.setText(title).setIcon(gridTab != null && gridTab.isDefault() ? null : icon); @@ -569,10 +815,38 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac public void saveUiState() { if (isStateBeingRestored()) return; + if (myOriginal != null) { + myOriginal.saveUiState(); + return; + } + int offset = updateTabsIndices(myTabs, 0); + for (RunnerContentUi child : myChildren) { + offset = updateTabsIndices(child.myTabs, offset); + } + + doSaveUiState(); + } + + private static int updateTabsIndices(final JBRunnerTabs tabs, int offset) { + for (TabInfo each : tabs.getTabs()) { + final int index = tabs.getIndexOf(each); + final TabImpl tab = getTabFor(each); + if (tab != null) tab.setIndex(index + offset); + } + return offset + tabs.getTabCount(); + } + + private void doSaveUiState() { + if (isStateBeingRestored()) return; + for (TabInfo each : myTabs.getTabs()) { GridImpl eachGrid = getGridFor(each); eachGrid.saveUiState(); } + + for (RunnerContentUi child : myChildren) { + child.doSaveUiState(); + } } @Nullable @@ -581,6 +855,17 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac return getTabFor(info); } + @Override + public void showNotify() { + final Window window = SwingUtilities.getWindowAncestor(myComponent); + if (window instanceof IdeFrame.Child) { + ((IdeFrame.Child)window).setFrameTitle(mySessionName); + } + } + + @Override + public void hideNotify() {} + @Nullable private static TabImpl getTabFor(@Nullable final TabInfo tab) { if (tab == null) { @@ -640,6 +925,10 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac if (myComponent.getRootPane() != null) { saveUiState(); } + if (myOriginal != null) { + myDisposing = true; + fireContentClosed(null); + } } public boolean canChangeSelectionTo(Content content, boolean implicit) { @@ -674,14 +963,25 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac } public void dispose() { - + if (myOriginal != null) { + myOriginal.myChildren.remove(this); + } } public void restoreLayout() { - Content[] all = myManager.getContents(); + final RunnerContentUi[] children = myChildren.toArray(new RunnerContentUi[myChildren.size()]); + final List contents = new ArrayList(); + Collections.addAll(contents, myManager.getContents()); + for (RunnerContentUi child : children) { + Collections.addAll(contents, child.myManager.getContents()); + } + Content[] all = contents.toArray(new Content[contents.size()]); setStateIsBeingRestored(true, this); try { + for (RunnerContentUi child : children) { + child.myManager.removeAllContents(false); + } myManager.removeAllContents(false); myMinimizedViewActions.removeAll(); } @@ -730,7 +1030,7 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac } public boolean isMinimizeActionEnabled() { - return myMinimizeActionEnabled; + return myMinimizeActionEnabled && myOriginal == null; } public boolean isMoveToGridActionEnabled() { @@ -820,12 +1120,12 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac public void addNotify() { super.addNotify(); - if (!myUiLastStateWasRestored) { + if (!myUiLastStateWasRestored && myOriginal == null) { myUiLastStateWasRestored = true; - //noinspection SSBasedInspection // [kirillk] this is done later since restoreUiState doesn't work properly in the addNotify call chain //todo to investigate and to fix (may cause extra flickering) + //noinspection SSBasedInspection SwingUtilities.invokeLater(new Runnable() { public void run() { restoreLastUiState().doWhenDone(new Runnable() { @@ -946,90 +1246,6 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac updateTabsUI(false); } - private static boolean willBeEmptyOnRemove(GridImpl grid, List toRemove) { - List attachedToGrid = grid.getAttachedContents(); - for (Content each : attachedToGrid) { - if (!toRemove.contains(each)) return false; - } - - return true; - } - - - public CellTransform.Restore detach(final Content[] content) { - List contents = Arrays.asList(content); - - for (Content each : content) { - GridImpl eachGrid = getGridFor(each, false); - if (willBeEmptyOnRemove(eachGrid, contents)) { - TabInfo info = myTabs.findInfo(eachGrid); - if (info != null) { - info.setHidden(true); - } - } - } - - updateTabsUI(true); - - return new CellTransform.Restore() { - public ActionCallback restoreInGrid() { - showHiddenTabs(); - updateTabsUI(true); - return new ActionCallback.Done(); - } - }; - } - - private void showHiddenTabs() { - List tabs = myTabs.getTabs(); - for (TabInfo eachInfos : tabs) { - GridImpl eachGrid = (GridImpl)eachInfos.getComponent(); - if (!eachGrid.getAttachedContents().isEmpty()) { - eachInfos.setHidden(false); - } - } - } - - public void moveToTab(final Content content) { - saveUiState(); - - setStateIsBeingRestored(true, this); - try { - myManager.removeContent(content, false); - getStateFor(content).assignTab(myLayoutSettings.createNewTab()); - getStateFor(content).setPlaceInGrid(PlaceInGrid.center); - myManager.addContent(content); - } - finally { - setStateIsBeingRestored(false, this); - } - - saveUiState(); - } - - public void moveToGrid(final Content content) { - saveUiState(); - - setStateIsBeingRestored(true, this); - - try { - myManager.removeContent(content, false); - getStateFor(content).assignTab(myLayoutSettings.getDefaultTab()); - getStateFor(content).setPlaceInGrid(myLayoutSettings.getDefaultGridPlace(content)); - myManager.addContent(content); - } - finally { - setStateIsBeingRestored(false, this); - } - - select(content, true).doWhenDone(new Runnable() { - public void run() { - saveUiState(); - } - }); - } - - public Project getProject() { return myProject; } @@ -1055,7 +1271,7 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac } public boolean isHorizontalToolbar() { - return myLayoutSettings.isToolbarHorizontal(); + return false; } public ActionCallback select(final Content content, final boolean requestFocus) { @@ -1068,18 +1284,12 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac final ActionCallback result = new ActionCallback(); - if (grid.isDetached(content)) { - if (requestFocus) { + myTabs.select(info, false).doWhenDone(new Runnable() { + public void run() { grid.select(content, requestFocus).notifyWhenDone(result); } - } - else { - myTabs.select(info, false).doWhenDone(new Runnable() { - public void run() { - grid.select(content, requestFocus).notifyWhenDone(result); - } - }); - } + }); + return result; } @@ -1233,4 +1443,142 @@ public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Fac return result; } + + + private int findFreeWindow() { + int i; + for (i = 1; i < Integer.MAX_VALUE; i++) { + if (!isUsed(i)) { + return i; + } + } + return i; + } + + private boolean isUsed(int i) { + for (RunnerContentUi child : myChildren) { + if (child.getWindow() == i) { + return true; + } + } + return false; + } + + private DockManagerImpl getDockManager() { + return (DockManagerImpl)DockManager.getInstance(myProject); + } + + class MyDragOutDelegate implements TabInfo.DragOutDelegate { + private DragSession mySession; + + @Override + public void dragOutStarted(MouseEvent mouseEvent, TabInfo info) { + final JComponent component = info.getComponent(); + final Content[] data = CONTENT_KEY.getData((DataProvider)component); + final List contents = Arrays.asList(data); + + storeDefaultIndices(data); + + final Dimension size = info.getComponent().getSize(); + final Image image = myTabs.getComponentImage(info); + if (component instanceof Grid) { + info.setHidden(true); + } + + Presentation presentation = new Presentation(info.getText()); + presentation.setIcon(info.getIcon()); + mySession = getDockManager().createDragSession(mouseEvent, new DockableGrid(image, presentation, + size, + contents, 0)); + } + + @Override + public void processDragOut(MouseEvent event, TabInfo source) { + mySession.process(event); + } + + @Override + public void dragOutFinished(MouseEvent event, TabInfo source) { + final Component component = event.getComponent(); + final IdeFrame window = UIUtil.getParentOfType(IdeFrame.class, component); + if (window != null) { + + } + mySession.process(event); + mySession = null; + } + } + + class DockableGrid implements DockableContent> { + final Image myImg; + private Presentation myPresentation; + private final Dimension myPreferredSize; + private final List myContents; + private final int myWindow; + + public DockableGrid(Image img, Presentation presentation, final Dimension size, List contents, int window) { + myImg = img; + myPresentation = presentation; + myPreferredSize = size; + myContents = contents; + myWindow = window; + } + + @Override + public List getKey() { + return myContents; + } + + @Override + public Image getPreviewImage() { + return myImg; + } + + @Override + public Dimension getPreferredSize() { + return myPreferredSize; + } + + @Override + public String getDockContainerType() { + return DockableGridContainerFactory.TYPE; + } + + @Override + public Presentation getPresentation() { + return myPresentation; + } + + public RunnerContentUi getRunnerUi() { + return RunnerContentUi.this; + } + + public RunnerContentUi getOriginalRunnerUi() { + return myOriginal != null ? myOriginal : RunnerContentUi.this; + } + + public List getContents() { + return myContents; + } + + @Override + public void close() { + } + + public int getWindow() { + return myWindow; + } + } + + void fireContentOpened(Content content) { + for (Listener each : myDockingListeners) { + each.contentAdded(content); + } + } + + void fireContentClosed(Content content) { + for (Listener each : myDockingListeners) { + each.contentRemoved(content); + } + } } diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerLayout.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerLayout.java index 5cc040503267..b336d791e504 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerLayout.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/RunnerLayout.java @@ -20,6 +20,7 @@ import com.intellij.execution.ui.layout.LayoutAttractionPolicy; import com.intellij.execution.ui.layout.PlaceInGrid; import com.intellij.execution.ui.layout.Tab; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.content.Content; @@ -33,7 +34,8 @@ import javax.swing.*; import java.util.*; public class RunnerLayout { - + public static final Key DEFAULT_INDEX = Key.create("RunnerLayoutDefaultIndex"); + public static final Key DROP_INDEX = Key.create("RunnerLayoutDropIndex"); private final String myID; protected Map myViews = new HashMap(); @@ -64,10 +66,6 @@ public class RunnerLayout { return tab; } - public TabImpl getDefaultTab() { - return getOrCreateTab(0); - } - private TabImpl createNewTab(final int index) { final TabImpl tab; @@ -153,7 +151,9 @@ public class RunnerLayout { } for (TabImpl eachTab : myTabs) { - eachTab.write(parentNode); + if (isUsed(eachTab)) { + eachTab.write(parentNode); + } } parentNode.addContent(XmlSerializer.serialize(myGeneral)); @@ -166,13 +166,13 @@ public class RunnerLayout { myViews.clear(); for (TabImpl each : myTabs) { - final TabImpl.Default defaultTab = getOrCreateDefaultTab(each.getIndex()); + final TabImpl.Default defaultTab = getOrCreateDefaultTab(each.getDefaultIndex()); each.copyFrom(defaultTab); } } public boolean isToolbarHorizontal() { - return myGeneral.horizontalToolbar; + return false; } public void setToolbarHorizontal(boolean horizontal) { diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/TabImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/TabImpl.java index 268029f68165..673c7bbe9898 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/TabImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/TabImpl.java @@ -40,6 +40,10 @@ public class TabImpl extends AbstractTab implements Tab { return myIndex; } + public int getDefaultIndex() { + return myDefaultIndex >= 0 ? myDefaultIndex : myIndex; + } + public String getDisplayName() { return myDisplayName; } @@ -52,6 +56,10 @@ public class TabImpl extends AbstractTab implements Tab { myIndex = index; } + public void setDefaultIndex(final int index) { + myDefaultIndex = index; + } + public void setDisplayName(final String displayName) { myDisplayName = displayName; } @@ -161,6 +169,7 @@ public class TabImpl extends AbstractTab implements Tab { public Default(final int index, final String displayName, final Icon icon) { myIndex = index; + myDefaultIndex = index; myDisplayName = displayName; myIcon = icon; } diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/ViewContextEx.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/ViewContextEx.java index 13d0e593c255..84282337391b 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/ViewContextEx.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/ViewContextEx.java @@ -16,14 +16,19 @@ package com.intellij.execution.ui.layout.impl; +import com.intellij.execution.ui.layout.GridCell; import com.intellij.execution.ui.layout.ViewContext; import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.util.ActionCallback; public interface ViewContextEx extends ViewContext { RunnerLayout getLayoutSettings(); ActionGroup getCellPopupGroup(String place); - void doWhenInitialized(Runnable runnable); + boolean isOriginal(); + + int getWindow(); + ActionCallback detachTo(int window, GridCell cell); } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/ViewImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/ViewImpl.java index 89673d9d600d..83b8d0833827 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/ViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/ViewImpl.java @@ -31,16 +31,19 @@ public class ViewImpl implements View { private Tab myTab; private int myTabIndex; + + private int myWindow; private PlaceInGrid myPlaceInGrid; private boolean myMinimizedInGrid; - public ViewImpl(String id, TabImpl tab, final PlaceInGrid placeInGrid, boolean minimizedInGrid) { + public ViewImpl(String id, TabImpl tab, final PlaceInGrid placeInGrid, boolean minimizedInGrid, int window) { myID = id; myTab = tab; myPlaceInGrid = placeInGrid; myMinimizedInGrid = minimizedInGrid; + myWindow = window; } public ViewImpl(RunnerLayout settings, Element element) { @@ -94,6 +97,16 @@ public class ViewImpl implements View { myTabIndex = tabIndex; } + @Override + public int getWindow() { + return myWindow; + } + + @Override + public void setWindow(int windowNumber) { + myWindow = windowNumber; + } + public static class Default { private final String myID; @@ -110,7 +123,7 @@ public class ViewImpl implements View { public ViewImpl createView(RunnerLayout settings) { final TabImpl tab = myTabID == Integer.MAX_VALUE ? settings.createNewTab() : settings.getOrCreateTab(myTabID); - return new ViewImpl(myID, tab, myPlaceInGrid, myMinimizedInGrid); + return new ViewImpl(myID, tab, myPlaceInGrid, myMinimizedInGrid, 0); } public PlaceInGrid getPlaceInGrid() { diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedFileAction.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedFileAction.java index e60a8ccc09a7..52134b1f406a 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedFileAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoRelatedFileAction.java @@ -32,6 +32,8 @@ import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.util.Processor; +import com.intellij.util.containers.hash.*; +import com.intellij.util.containers.hash.HashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,6 +41,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.util.*; +import java.util.HashMap; import java.util.List; /** @@ -207,7 +210,7 @@ public class GotoRelatedFileAction extends AnAction { } } - List items = new ArrayList(); + Set items = new HashSet (); for (GotoRelatedProvider provider : Extensions.getExtensions(GotoRelatedProvider.EP_NAME)) { items.addAll(provider.getItems(contextElement)); @@ -215,7 +218,7 @@ public class GotoRelatedFileAction extends AnAction { items.addAll(provider.getItems(dataContext)); } } - return items; + return new ArrayList(items); } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplateManagerImpl.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplateManagerImpl.java index 323560f5efff..e39d357b7d34 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplateManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FileTemplateManagerImpl.java @@ -48,6 +48,7 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.text.MessageFormat; +import java.text.SimpleDateFormat; import java.util.*; /** @@ -319,10 +320,17 @@ public class FileTemplateManagerImpl extends FileTemplateManager implements Expo @NonNls Properties props = new Properties(); Calendar calendar = Calendar.getInstance(); + Date date = new Date(calendar.getTimeInMillis()); + SimpleDateFormat sdfMonthNameShort = new SimpleDateFormat("MMM"); + SimpleDateFormat sdfMonthNameFull = new SimpleDateFormat("MMMM"); + SimpleDateFormat sdfYearFull = new SimpleDateFormat("yyyy"); + props.setProperty("DATE", DateFormatUtil.formatDate(calendar.getTime())); props.setProperty("TIME", DateFormatUtil.formatTime(calendar.getTime())); - props.setProperty("YEAR", Integer.toString(calendar.get(Calendar.YEAR))); + props.setProperty("YEAR", sdfYearFull.format(date)); props.setProperty("MONTH", getCalendarValue(calendar, Calendar.MONTH)); + props.setProperty("MONTH_NAME_SHORT", sdfMonthNameShort.format(date)); + props.setProperty("MONTH_NAME_FULL", sdfMonthNameFull.format(date)); props.setProperty("DAY", getCalendarValue(calendar, Calendar.DAY_OF_MONTH)); props.setProperty("HOUR", getCalendarValue(calendar, Calendar.HOUR_OF_DAY)); props.setProperty("MINUTE", getCalendarValue(calendar, Calendar.MINUTE)); diff --git a/platform/lang-impl/src/com/intellij/ide/todo/TodoTreeBuilder.java b/platform/lang-impl/src/com/intellij/ide/todo/TodoTreeBuilder.java index a0c783cfaf65..156ee77bd161 100644 --- a/platform/lang-impl/src/com/intellij/ide/todo/TodoTreeBuilder.java +++ b/platform/lang-impl/src/com/intellij/ide/todo/TodoTreeBuilder.java @@ -539,6 +539,7 @@ public abstract class TodoTreeBuilder extends AbstractTreeBuilder { return null; } Object[] children = getTreeStructure().getChildElements(parent); + Arrays.sort(children, getUi().getNodeDescriptorComparator()); int idx = -1; for (int i = 0; i < children.length; i++) { if (obj.equals(children[i])) { @@ -584,6 +585,7 @@ public abstract class TodoTreeBuilder extends AbstractTreeBuilder { return null; } Object[] children = getTreeStructure().getChildElements(parent); + Arrays.sort(children, getUi().getNodeDescriptorComparator()); int idx = -1; for (int i = 0; i < children.length; i++) { if (obj.equals(children[i])) { diff --git a/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java b/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java index 822ec3a804dc..2285504fe806 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java +++ b/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java @@ -228,18 +228,20 @@ public class FileStructurePopup implements Disposable { for (ObjectWithWeight p : paths) { final Object last = ((TreePath)p.node).getLastPathComponent(); final List elements = new ArrayList(); - FilteringTreeStructure.FilteringNode node = - (FilteringTreeStructure.FilteringNode)((DefaultMutableTreeNode)last).getUserObject(); - while (node != null) { - elements.add(getPsi(node)); - node = node.getParentNode(); - } - final int size = ContainerUtil.intersection(parents, elements).size(); - if (size > max) { - max = size; - cur = p.node; - } else if (size == max && size == parents.size()) { - cur = p.node; + final Object object = ((DefaultMutableTreeNode)last).getUserObject(); + if (object instanceof FilteringTreeStructure.FilteringNode) { + FilteringTreeStructure.FilteringNode node = (FilteringTreeStructure.FilteringNode)object; + while (node != null) { + elements.add(getPsi(node)); + node = node.getParentNode(); + } + final int size = ContainerUtil.intersection(parents, elements).size(); + if (size > max) { + max = size; + cur = p.node; + } else if (size == max && size == parents.size()) { + cur = p.node; + } } } diff --git a/platform/lang-impl/src/com/intellij/psi/stubs/SerializedStubTree.java b/platform/lang-impl/src/com/intellij/psi/stubs/SerializedStubTree.java index abaf63828b69..d7423c8420b2 100644 --- a/platform/lang-impl/src/com/intellij/psi/stubs/SerializedStubTree.java +++ b/platform/lang-impl/src/com/intellij/psi/stubs/SerializedStubTree.java @@ -19,7 +19,8 @@ */ package com.intellij.psi.stubs; -import java.io.ByteArrayInputStream; +import com.intellij.util.io.UnsyncByteArrayInputStream; + import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; @@ -45,7 +46,7 @@ public class SerializedStubTree { } public StubElement getStub() { - return SerializationManager.getInstance().deserialize(new ByteArrayInputStream(myBytes)); + return SerializationManager.getInstance().deserialize(new UnsyncByteArrayInputStream(myBytes)); } public boolean equals(final Object that) { 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 6997d30dbed7..ced3ab62d05d 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 @@ -3570,6 +3570,10 @@ public class AbstractTreeUi { } } + public Comparator getNodeDescriptorComparator() { + return myNodeDescriptorComparator; + } + private void disposeNode(DefaultMutableTreeNode node) { TreeNode parent = node.getParent(); if (parent instanceof DefaultMutableTreeNode) { diff --git a/platform/platform-api/src/com/intellij/notification/Notification.java b/platform/platform-api/src/com/intellij/notification/Notification.java index 91c23432ba70..36ad4145c9e7 100644 --- a/platform/platform-api/src/com/intellij/notification/Notification.java +++ b/platform/platform-api/src/com/intellij/notification/Notification.java @@ -18,6 +18,8 @@ package com.intellij.notification; import com.intellij.openapi.diagnostic.Logger; 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 org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -97,6 +99,11 @@ public class Notification { public void expire() { NotificationsManager.getNotificationsManager().expire(this); + hideBalloon(); + myExpired = true; + } + + public void hideBalloon() { if (myBalloonRef != null) { final Balloon balloon = myBalloonRef.get(); if (balloon != null) { @@ -104,15 +111,20 @@ public class Notification { } myBalloonRef = null; } - myExpired = true; } - public void setBalloon(@Nullable final Balloon balloon) { - if (balloon != null) { - myBalloonRef = new WeakReference(balloon); - } else { - myBalloonRef = null; - } + public void setBalloon(@NotNull final Balloon balloon) { + hideBalloon(); + myBalloonRef = new WeakReference(balloon); + balloon.addListener(new JBPopupAdapter() { + @Override + public void onClosed(LightweightWindowEvent event) { + WeakReference ref = myBalloonRef; + if (ref != null && ref.get() == balloon) { + myBalloonRef = null; + } + } + }); } @Nullable diff --git a/platform/platform-api/src/com/intellij/ui/TableScrollingUtil.java b/platform/platform-api/src/com/intellij/ui/TableScrollingUtil.java index d3fd4590a245..e3993f86206d 100644 --- a/platform/platform-api/src/com/intellij/ui/TableScrollingUtil.java +++ b/platform/platform-api/src/com/intellij/ui/TableScrollingUtil.java @@ -272,6 +272,17 @@ public class TableScrollingUtil { moveEnd(list); } }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0)), list); + + new AnAction() { + public void actionPerformed(AnActionEvent e) { + moveHome(list); + } + }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0)), list); + new AnAction() { + public void actionPerformed(AnActionEvent e) { + moveEnd(list); + } + }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0)), list); } } diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java index f599a1d8bcd2..25fb02896813 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainer.java @@ -30,7 +30,7 @@ public interface DockContainer extends Disposable, Activatable { RelativeRectangle getAcceptArea(); boolean canAccept(DockableContent content, RelativePoint point); - JComponent getComponent(); + JComponent getContainerComponent(); void add(DockableContent content, RelativePoint dropTarget); diff --git a/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java index 3dcc059e3de2..6e7e581c61d7 100644 --- a/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java +++ b/platform/platform-api/src/com/intellij/ui/docking/DockContainerFactory.java @@ -20,7 +20,7 @@ import org.jdom.Element; public interface DockContainerFactory extends Disposable { - DockContainer createContainer(); + DockContainer createContainer(DockableContent content); interface Persistent extends DockContainerFactory { diff --git a/platform/platform-api/src/com/intellij/ui/tabs/TabsListener.java b/platform/platform-api/src/com/intellij/ui/tabs/TabsListener.java index ff798575ac6a..a9bba15c4d37 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/TabsListener.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/TabsListener.java @@ -22,12 +22,18 @@ public interface TabsListener { void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection); + void tabsMoved(); + class Adapter implements TabsListener { public void selectionChanged(TabInfo oldSelection, TabInfo newSelection) { } public void beforeSelectionChanged(TabInfo oldSelection, TabInfo newSelection) { } + + @Override + public void tabsMoved() { + } } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/ActionPanel.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/ActionPanel.java index f4ae28f9729e..9dcd06843d5a 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/ActionPanel.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/ActionPanel.java @@ -30,7 +30,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -class ActionPanel extends NonOpaquePanel { +public class ActionPanel extends NonOpaquePanel { private final ActionGroup myGroup; private final List myButtons = new ArrayList(); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java index 94c737a0ff74..70428d60ca50 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java @@ -253,6 +253,9 @@ class DragHelper extends MouseDragHelper { myDragRec = null; myTabs.resetTabsCache(); + if (!willDragOutStart) { + myTabs.fireTabsMoved(); + } myTabs.relayout(true, false); myTabs.revalidate(); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 3484decdc008..6d49d1718d60 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -163,7 +163,7 @@ public class JBTabsImpl extends JComponent private boolean myOwnSwitchProvider = true; private SwitchProvider mySwitchDelegate; - private TabInfo myDropInfo; + protected TabInfo myDropInfo; private int myDropInfoIndex; private TabInfo myOldSelection; @@ -629,7 +629,7 @@ public class JBTabsImpl extends JComponent } info.getChangeSupport().addPropertyChangeListener(this); - final TabLabel label = new TabLabel(this, info); + final TabLabel label = createTabLabel(info); myInfo2Label.put(info, label); if (!isDropTarget) { @@ -674,6 +674,9 @@ public class JBTabsImpl extends JComponent return info; } + protected TabLabel createTabLabel(TabInfo info) { + return new TabLabel(this, info); + } @NotNull public TabInfo addTab(TabInfo info) { @@ -832,6 +835,14 @@ public class JBTabsImpl extends JComponent } } + void fireTabsMoved() { + for (TabsListener eachListener : myTabListeners) { + if (eachListener != null) { + eachListener.tabsMoved(); + } + } + } + private ActionCallback requestFocus(final JComponent toFocus) { if (toFocus == null) return new ActionCallback.Done(); @@ -1745,17 +1756,18 @@ public class JBTabsImpl extends JComponent return myInfo2Label.get(getSelectedInfo()); } - static class ShapeInfo { - ShapeTransform path; - ShapeTransform fillPath; - ShapeTransform labelPath; - int labelBottomY; - int labelTopY; - int labelLeftX; - int labelRightX; - Insets insets; - Color from; - Color to; + protected static class ShapeInfo { + public ShapeInfo() {} + public ShapeTransform path; + public ShapeTransform fillPath; + public ShapeTransform labelPath; + public int labelBottomY; + public int labelTopY; + public int labelLeftX; + public int labelRightX; + public Insets insets; + public Color from; + public Color to; } @@ -2977,7 +2989,7 @@ public class JBTabsImpl extends JComponent } } - public static Rectangle layout(JComponent c, Rectangle bounds) { + public Rectangle layout(JComponent c, Rectangle bounds) { final Rectangle now = c.getBounds(); if (!bounds.equals(now)) { c.setBounds(bounds); @@ -2987,7 +2999,7 @@ public class JBTabsImpl extends JComponent return bounds; } - public static Rectangle layout(JComponent c, int x, int y, int width, int height) { + public Rectangle layout(JComponent c, int x, int y, int width, int height) { return layout(c, new Rectangle(x, y, width, height)); } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java index 54dd2366b569..63237e5e51b8 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/TabLabel.java @@ -43,7 +43,7 @@ import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; public class TabLabel extends JPanel { - private final SimpleColoredComponent myLabel = new SimpleColoredComponent() { + protected final SimpleColoredComponent myLabel = new SimpleColoredComponent() { @Override protected boolean shouldDrawMacShadow() { return SystemInfo.isMac; @@ -54,7 +54,7 @@ public class TabLabel extends JPanel { private Icon myOverlayedIcon; private final TabInfo myInfo; - private ActionPanel myActionPanel; + protected ActionPanel myActionPanel; private boolean myCentered; private final Wrapper myLabelPlaceholder = new Wrapper(); @@ -224,7 +224,7 @@ public class TabLabel extends JPanel { }); } - private int getNonSelectedOffset() { + protected int getNonSelectedOffset() { if (myTabs.isEditorTabs()) { int offset = (TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT / 2); if (myTabs.isSingleRow()) { @@ -237,7 +237,7 @@ public class TabLabel extends JPanel { return 2; } - private int getSelectedOffset() { + protected int getSelectedOffset() { return myTabs.isEditorTabs() ? -(TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT / 2) + 1 : 1; } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/UndoRedoAction.java b/platform/platform-impl/src/com/intellij/ide/actions/UndoRedoAction.java index 0fc43b2ee49a..0d09446cef7a 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/UndoRedoAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/UndoRedoAction.java @@ -53,7 +53,11 @@ public abstract class UndoRedoAction extends DumbAwareAction { } UndoManager undoManager = getUndoManager(editor, dataContext); - presentation.setEnabled(undoManager != null && isAvailable(editor, undoManager)); + if (undoManager == null) { + presentation.setEnabled(false); + return; + } + presentation.setEnabled(isAvailable(editor, undoManager)); Pair pair = getActionNameAndDescription(editor, undoManager); diff --git a/platform/platform-impl/src/com/intellij/idea/SocketLock.java b/platform/platform-impl/src/com/intellij/idea/SocketLock.java index d8ab49cb1712..ca9ebb6a06a2 100644 --- a/platform/platform-impl/src/com/intellij/idea/SocketLock.java +++ b/platform/platform-impl/src/com/intellij/idea/SocketLock.java @@ -162,13 +162,13 @@ public class SocketLock { try { DataOutputStream out = new DataOutputStream(socket.getOutputStream()); out.writeUTF(ACTIVATE_COMMAND + StringUtil.join(args, "\0")); + out.flush(); String response = in.readUTF(); if (response.equals("ok")) { return ActivateStatus.ACTIVATED; } } catch(IOException e) { - } return ActivateStatus.CANNOT_ACTIVATE; } diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java index 81e873ae3041..e30f09e9611a 100644 --- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java +++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java @@ -111,7 +111,17 @@ public class StartupUtil { } if (activateStatus != SocketLock.ActivateStatus.NO_INSTANCE) { - showError("Error", "Only one instance of " + ApplicationNamesInfo.getInstance().getFullProductName() + " can be run at a time."); + if (isHeadless()) { //team server inspections + System.out.println("Only one instance of " + ApplicationNamesInfo.getInstance().getFullProductName() + " can be run at a time."); + return false; + } + if (activateStatus == SocketLock.ActivateStatus.CANNOT_ACTIVATE) { + JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), + "Only one instance of " + ApplicationNamesInfo.getInstance().getFullProductName() + + " can be run at a time.", + "Error", + JOptionPane.INFORMATION_MESSAGE); + } return false; } diff --git a/platform/platform-impl/src/com/intellij/notification/EventLog.java b/platform/platform-impl/src/com/intellij/notification/EventLog.java index 24552d8947ce..2d1e660c2c6d 100644 --- a/platform/platform-impl/src/com/intellij/notification/EventLog.java +++ b/platform/platform-impl/src/com/intellij/notification/EventLog.java @@ -133,6 +133,7 @@ public class EventLog implements Notifications { mainText += content; } + mainText = StringUtil.replace(mainText, " ", " "); int nlIndex = eolIndex(mainText); if (nlIndex >= 0) { mainText = mainText.substring(0, nlIndex); @@ -329,6 +330,7 @@ public class EventLog implements Notifications { group.add(new DumbAwareAction("Mark all as read", "Mark all unread notifications as read", IconLoader.getIcon("/general/reset.png")) { @Override public void update(AnActionEvent e) { + if (project.isDisposed()) return; e.getPresentation().setEnabled(!getProjectComponent(project).myProjectModel.getNotifications().isEmpty()); } diff --git a/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java b/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java index 6870f9459f21..50968d23171b 100644 --- a/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java @@ -314,13 +314,6 @@ public class NotificationsManagerImpl extends NotificationsManager implements No .setHideOnKeyOutside(hideOnClickOutside).setHideOnFrameResize(false); final Balloon balloon = builder.createBalloon(); - balloon.addListener(new JBPopupAdapter() { - @Override - public void onClosed(LightweightWindowEvent event) { - notification.setBalloon(null); - } - }); - notification.setBalloon(balloon); return balloon; } diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandProcessorImpl.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandProcessorImpl.java index 71494e542fac..33596a4f619d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandProcessorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/CommandProcessorImpl.java @@ -57,6 +57,11 @@ public class CommandProcessorImpl extends CommandProcessorEx { myUndoConfirmationPolicy = undoConfirmationPolicy; myDocument = document; } + + @Override + public String toString() { + return "'" + myName + "', group: '" + myGroupId + "'"; + } } private CommandDescriptor myCurrentCommand = null; @@ -219,7 +224,8 @@ public class CommandProcessorImpl extends CommandProcessorEx { } public void leaveModal() { - CommandLog.LOG.assertTrue(myCurrentCommand == null); + CommandLog.LOG.assertTrue(myCurrentCommand == null, "Command must not run: " + String.valueOf(myCurrentCommand)); + myCurrentCommand = myInterruptedCommands.pop(); if (myCurrentCommand != null) { fireCommandStarted(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java index f41ac78fee30..e2340d7f1489 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorContainerFactory.java @@ -22,6 +22,7 @@ import com.intellij.openapi.wm.IdeFrame; import com.intellij.ui.docking.DockContainer; import com.intellij.ui.docking.DockContainerFactory; import com.intellij.ui.docking.DockManager; +import com.intellij.ui.docking.DockableContent; import org.jdom.Element; public class DockableEditorContainerFactory implements DockContainerFactory.Persistent { @@ -39,7 +40,7 @@ public class DockableEditorContainerFactory implements DockContainerFactory.Pers } @Override - public DockContainer createContainer() { + public DockContainer createContainer(DockableContent content) { return createContainer(false); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java index 2eacfab54920..11c212bc71ca 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/DockableEditorTabbedContainer.java @@ -187,7 +187,7 @@ public class DockableEditorTabbedContainer implements DockContainer.Persistent { } @Override - public JComponent getComponent() { + public JComponent getContainerComponent() { return mySplitters; } @@ -196,7 +196,7 @@ public class DockableEditorTabbedContainer implements DockContainer.Persistent { } public void close(VirtualFile file) { - mySplitters.getCurrentWindow().closeFile(file); + mySplitters.closeFile(file, false); } @Override 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 776e61846800..d64dd34acb6c 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 @@ -586,6 +586,31 @@ public class EditorsSplitters extends JPanel { return null; } + void closeFile(VirtualFile file, boolean moveFocus) { + final List windows = findWindows(file); + if (!windows.isEmpty()) { + final VirtualFile nextFile = findNextFile(file); + for (final EditorWindow window : windows) { + LOG.assertTrue(window.getSelectedEditor() != null); + window.closeFile(file, false, moveFocus); + if (window.getTabCount() == 0 && nextFile != null) { + EditorWithProviderComposite newComposite = myManager.newEditorComposite(nextFile); + window.setEditor(newComposite, moveFocus); // newComposite can be null + } + } + // cleanup windows with no tabs + for (final EditorWindow window : windows) { + if (window.isDisposed()) { + // call to window.unsplit() which might make its sibling disposed + continue; + } + if (window.getTabCount() == 0) { + window.unsplit(false); + } + } + } + } + private final class MyFocusTraversalPolicy extends IdeFocusTraversalPolicy { public final Component getDefaultComponentImpl(final Container focusCycleRoot) { if (myCurrentWindow != null) { 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 582716f561ed..f1881c6cb742 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 @@ -556,33 +556,12 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec assertDispatchThread(); runChange(new FileEditorManagerChange() { public void run(EditorsSplitters splitters) { - final List windows = splitters.findWindows(file); - if (!windows.isEmpty()) { - final VirtualFile nextFile = splitters.findNextFile(file); - for (final EditorWindow window : windows) { - LOG.assertTrue(window.getSelectedEditor() != null); - window.closeFile(file, false, moveFocus); - if (window.getTabCount() == 0 && nextFile != null) { - EditorWithProviderComposite newComposite = newEditorComposite(nextFile); - window.setEditor(newComposite, moveFocus); // newComposite can be null - } - } - // cleanup windows with no tabs - for (final EditorWindow window : windows) { - if (window.isDisposed()) { - // call to window.unsplit() which might make its sibling disposed - continue; - } - if (window.getTabCount() == 0) { - window.unsplit(false); - } - } - } + splitters.closeFile(file, moveFocus); } }, closeAllCopies ? null : getActiveSplitters(true).getResult()); } -//-------------------------------------- Open File ---------------------------------------- + //-------------------------------------- Open File ---------------------------------------- @NotNull public Pair openFileWithProviders(@NotNull final VirtualFile file, @@ -865,7 +844,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec @Nullable - private EditorWithProviderComposite newEditorComposite(final VirtualFile file) { + EditorWithProviderComposite newEditorComposite(final VirtualFile file) { if (file == null) { return null; } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java index c9f1f5c6ef0e..b5eca8b1f0e4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/GlassPaneDialogWrapperPeer.java @@ -220,11 +220,11 @@ public class GlassPaneDialogWrapperPeer extends DialogWrapperPeer implements Foc } public boolean isVisible() { - return myDialog.isVisible(); + return myDialog != null && myDialog.isVisible(); } public boolean isShowing() { - return myDialog.isShowing(); + return myDialog != null && myDialog.isShowing(); } public void setSize(final int width, final int height) { diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java index 38eeed18a5df..915ca59e82b8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemBase.java @@ -188,6 +188,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { return ioFile; } + // todo[r.sh] use NIO2 API after migration to JDK 7 // returns -1 if could not get attributes @MagicConstant(flags = {BA_EXISTS, BA_REGULAR, BA_DIRECTORY, BA_HIDDEN}) private static int getBooleanAttributes(@NotNull File f) { @@ -196,8 +197,7 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { Object flags = JAVA_IO_FILESYSTEM_GET_BOOLEAN_ATTRIBUTES_METHOD.invoke(JAVA_IO_FILESYSTEM, f); return ((Integer)flags).intValue(); } - catch (Exception ignored) { - } + catch (Exception ignored) { } } return -1; } @@ -535,7 +535,9 @@ public abstract class LocalFileSystemBase extends LocalFileSystem { public byte[] contentsToByteArray(@NotNull final VirtualFile file) throws IOException { final FileInputStream stream = new FileInputStream(convertToIOFileAndCheck(file)); try { - return FileUtil.loadBytes(stream, (int)file.getLength()); + final int length = (int)file.getLength(); + assert length >= 0 : file; + return FileUtil.loadBytes(stream, length); } finally { stream.close(); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFS.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFS.java index 9623d30cec49..9a2de59babeb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFS.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFS.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -508,7 +508,9 @@ public class PersistentFS extends ManagingFS implements ApplicationComponent { } else { try { - return FileUtil.loadBytes(contentStream, (int)file.getLength()); + final int length = (int)file.getLength(); + assert length >= 0 : file; + return FileUtil.loadBytes(contentStream, length); } catch (IOException e) { throw FSRecords.handleError(e); 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 9f9700fc1987..b490ba433f58 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 @@ -122,7 +122,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo @Override public IdeFrame getIdeFrame(DockContainer container) { - Component parent = UIUtil.findUltimateParent(container.getComponent()); + Component parent = UIUtil.findUltimateParent(container.getContainerComponent()); if (parent instanceof IdeFrame) { return (IdeFrame)parent; } @@ -143,7 +143,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo if (c == null) return null; for (DockContainer eachContainer : myContainers) { - if (SwingUtilities.isDescendingFrom(c, eachContainer.getComponent())) { + if (SwingUtilities.isDescendingFrom(c, eachContainer.getContainerComponent())) { return eachContainer; } } @@ -152,7 +152,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo if (parent == null) return null; for (DockContainer eachContainer : myContainers) { - if (parent == UIUtil.findUltimateParent(eachContainer.getComponent())) { + if (parent == UIUtil.findUltimateParent(eachContainer.getContainerComponent())) { return eachContainer; } } @@ -354,8 +354,8 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo return myFactories.get(type); } - private void createNewDockContainerFor(DockableContent content, RelativePoint point) { - DockContainer container = getFactory(content.getDockContainerType()).createContainer(); + public void createNewDockContainerFor(DockableContent content, RelativePoint point) { + DockContainer container = getFactory(content.getDockContainerType()).createContainer(content); register(container); final DockWindow window = createWindowFor(null, container); @@ -424,7 +424,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo center.add(myNorthPanel, BorderLayout.NORTH); myDockContentUiContainer = new NonOpaquePanel(new BorderLayout()); - myDockContentUiContainer.add(myContainer.getComponent(), BorderLayout.CENTER); + myDockContentUiContainer.add(myContainer.getContainerComponent(), BorderLayout.CENTER); center.add(myDockContentUiContainer, BorderLayout.CENTER); myUiContainer.add(center, BorderLayout.CENTER); diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 4b218b9198be..fba31187d44c 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -227,7 +227,7 @@ group.reopen.mac.text=Open _Recent group.$LRU.description=List of recently opened projects action.CloseProject.text=Close Pro_ject action.CloseProject.description=Close current project -action.ShowSettings.text=Se_ttings +action.ShowSettings.text=Se_ttings... action.ShowSettings.description=Configure application settings action.ShowProjectStructureSettings.text=Pro_ject Structure... action.ShowProjectStructureSettings.description=Configure project structure @@ -1147,7 +1147,7 @@ group.EditSelectGroup.text=Editor Select Actions group.EditCreateDeleteGroup.text=Editor Create/Delete Actions group.CutCopyPasteGroup.text=Cut/Copy/Paste Actions group.EditSelectWordGroup.text=Select Word Actions -group.EditBookmarksGroup.text=Bookmarks +group.EditBookmarksGroup.text=Boo_kmarks group.OpenProjectGroup.text=Open Project Actions group.EditSmartGroup.text=Edit Code Actions group.ProjectViewGroup.text=Project View Actions diff --git a/platform/platform-resources-en/src/messages/ExecutionBundle.properties b/platform/platform-resources-en/src/messages/ExecutionBundle.properties index 3be53cf875b1..0181271196aa 100644 --- a/platform/platform-resources-en/src/messages/ExecutionBundle.properties +++ b/platform/platform-resources-en/src/messages/ExecutionBundle.properties @@ -133,6 +133,8 @@ tests.passed.with.warnings.message=Tests passed (with warnings) junit.run.hide.passed.action.name=Hide Passed junit.run.hide.passed.action.description=Hide passed tests junit.runing.info.track.test.action.name=Track Running Test +junit.runing.info.sort.alphabetically.action.name=Sort Alphabetically +junit.runing.info.sort.alphabetically.action.description=Sort tests or suites alphabetically junit.runing.info.track.test.action.description=Select currently running test in tree junit.runing.info.collapse.test.action.name=Collapse all test suites junit.runing.info.expand.test.action.name=Expand all test suites diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index 83c2df79e895..12a87ccd6ed0 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -728,18 +728,14 @@ - - - + + + - - - - - @@ -753,10 +749,6 @@ - - - - diff --git a/platform/platform-tests/testSrc/com/intellij/notification/EventLogTest.groovy b/platform/platform-tests/testSrc/com/intellij/notification/EventLogTest.groovy new file mode 100644 index 000000000000..f0ac1b26be4c --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/notification/EventLogTest.groovy @@ -0,0 +1,30 @@ +/* + * 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.notification; + +import com.intellij.testFramework.UsefulTestCase; + +/** + * @author peter + */ +class EventLogTest extends UsefulTestCase { + + public void testNbsp() { + def entry = EventLog.formatForLog(new Notification("xxx", "Title", "Hello world", NotificationType.ERROR)) + assert entry.message == 'Title: Hello world' + } + +} diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/FileDocumentManagerImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/FileDocumentManagerImplTest.java new file mode 100644 index 000000000000..baed1c53c602 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/FileDocumentManagerImplTest.java @@ -0,0 +1,580 @@ +package com.intellij.openapi.fileEditor; + +import com.intellij.codeStyle.CodeStyleFacade; +import com.intellij.codeStyle.DefaultCodeStyleFacade; +import com.intellij.mock.MockCommandProcessor; +import com.intellij.mock.MockEditorFactory; +import com.intellij.mock.MockVirtualFile; +import com.intellij.mock.MockVirtualFileManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.command.UndoConfirmationPolicy; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.ex.DocumentEx; +import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl; +import com.intellij.openapi.fileTypes.*; +import com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl; +import com.intellij.openapi.fileTypes.impl.InternalFileTypeFactory; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectLocator; +import com.intellij.openapi.project.impl.DefaultProjectLocator; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.testFramework.MockSchemesManagerFactory; +import com.intellij.testFramework.PlatformLiteFixture; +import com.intellij.util.LocalTimeCounter; +import org.jetbrains.annotations.NotNull; + +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.util.Arrays; + +public class FileDocumentManagerImplTest extends PlatformLiteFixture { + private MyFileDocumentManagerImpl myDocumentManager; + + @Override + protected void setUp() throws Exception { + super.setUp(); + initApplication(); + registerExtension(FileTypeFactory.FILE_TYPE_FACTORY_EP, new InternalFileTypeFactory()); + registerExtensionPoint(FileDocumentSynchronizationVetoer.EP_NAME, FileDocumentSynchronizationVetoer.class); + getApplication().registerService(CommandProcessor.class, new MyMockCommandProcessor()); + getApplication().registerService(CodeStyleFacade.class, new DefaultCodeStyleFacade()); + getApplication().registerService(ProjectLocator.class, new DefaultProjectLocator()); + + MockEditorFactory editorFactory = new MockEditorFactory(); + getApplication().registerService(EditorFactory.class, editorFactory); + final LanguageFileType[] fileType = {null}; + getApplication().addComponent(FileTypeManager.class, new FileTypeManagerImpl(null, new MockSchemesManagerFactory()){ + @NotNull + @Override + public FileType getFileTypeByFileName(@NotNull String fileName) { + return fileType[0]; + } + + @NotNull + @Override + public FileType getFileTypeByFile(@NotNull VirtualFile file) { + return fileType[0]; + } + + @NotNull + @Override + public FileType getFileTypeByExtension(@NotNull String extension) { + return fileType[0]; + } + }); + + fileType[0] = StdFileTypes.JAVA; + + getApplication().getComponent(FileTypeManager.class); + myDocumentManager = new MyFileDocumentManagerImpl(); + getApplication().registerService(FileDocumentManager.class, myDocumentManager); + } + + public void testGetCachedDocument_Cached() throws Exception { + final Document cachedDocument = myDocumentManager.getCachedDocument(new MockVirtualFile("test.txt")); + assertNull(cachedDocument); + } + + public void testGetCachedDocument_NotCached() throws Exception { + final VirtualFile file = newTextFile(); + final Document document = myDocumentManager.getDocument(file); + + assertSame(myDocumentManager.getCachedDocument(file), document); + } + + public void testGetDocument_CreateNew() throws Exception { + final VirtualFile file = newTextFile(); + final Document document = myDocumentManager.getDocument(file); + assertNotNull(document); + assertEquals("test", document.getText()); + assertEquals(file.getModificationStamp(), document.getModificationStamp()); + assertEquals(file.isWritable(), document.isWritable()); + } + + public void testGetDocument_CreateNew_ReadOnly() throws Exception { + final MockVirtualFile file = newTextFile(); + file.setWritable(false); + final Document document = myDocumentManager.getDocument(file); + assertNotNull(document); + assertEquals("test", document.getText()); + assertEquals(file.getModificationStamp(), document.getModificationStamp()); + assertEquals(file.isWritable(), document.isWritable()); + } + + public void testGetDocument_ReturnCachedValueTwice() throws Exception { + final VirtualFile file = newTextFile(); + final Document document = myDocumentManager.getDocument(file); + assertNotNull(document); + assertEquals("test", document.getText()); + + final Document document2 = myDocumentManager.getDocument(file); + assertSame(document2, document); + } + + public void testGetDocument_CreatesNewAfterGced() throws Exception { + final VirtualFile file = newTextFile(); + Document document = myDocumentManager.getDocument(file); + int idCode = System.identityHashCode(document); + //noinspection UnusedAssignment + document = null; + + System.gc(); + System.gc(); + + document = myDocumentManager.getDocument(file); + assertTrue(idCode != System.identityHashCode(document)); + } + + public void testGetUnsavedDocuments_NoDocuments() throws Exception { + final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments(); + assertEquals(0, unsavedDocuments.length); + } + + public void testGetUnsavedDocuments_CreatedDocument() throws Exception { + final VirtualFile file = newTextFile(); + myDocumentManager.getDocument(file); + + final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments(); + assertEquals(0, unsavedDocuments.length); + } + + public void testGetUnsavedDocuments_ModifiedDocument() throws Exception { + final VirtualFile file = newTextFile(); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + + final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments(); + assertEquals(1, unsavedDocuments.length); + assertSame(document, unsavedDocuments[0]); + assertTrue(Arrays.equals("test".getBytes(), file.contentsToByteArray())); + } + + public void testGetUnsavedDocuments_afterSaveAllDocuments() throws Exception { + final VirtualFile file = newTextFile(); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + + myDocumentManager.saveAllDocuments(); + final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments(); + assertEquals(0, unsavedDocuments.length); + } + + public void testGetUnsavedDocuments_afterSaveDocuments() throws Exception { + final VirtualFile file = newTextFile(); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + + myDocumentManager.saveDocument(document); + final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments(); + assertEquals(0, unsavedDocuments.length); + } + + private static MockVirtualFile newTextFile() { + return new MockVirtualFile("test.txt", "test"); + } + + public void testGetUnsavedDocuments_afterSaveDocumentWithProblems() throws Exception { + final VirtualFile file = new MockVirtualFile("test.txt", "test") { + @Override + @NotNull + public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException { + throw new IOException(""); + } + }; + + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + + myDocumentManager.saveDocument(document); + + assertNotNull(myDocumentManager.myExceptionOnSave); + + final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments(); + assertEquals(1, unsavedDocuments.length); + assertSame(document, unsavedDocuments[0]); + assertTrue(Arrays.equals("test".getBytes(), file.contentsToByteArray())); + } + + public void testUnsavedDocument_DontGc() throws Exception { + final VirtualFile file = newTextFile(); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + int idCode = System.identityHashCode(document); + //noinspection UnusedAssignment + document = null; + + System.gc(); + System.gc(); + + document = myDocumentManager.getDocument(file); + assertEquals(idCode, System.identityHashCode(document)); + } + + public void testUnsavedDocument_GcAfterSave() throws Exception { + final VirtualFile file = newTextFile(); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + int idCode = System.identityHashCode(document); + //noinspection UnusedAssignment + document = null; + + myDocumentManager.saveAllDocuments(); + + System.gc(); + System.gc(); + + document = myDocumentManager.getDocument(file); + assertTrue(idCode != System.identityHashCode(document)); + } + + public void testSaveDocument_DocumentWasntChanged() throws Exception { + final VirtualFile file = newTextFile(); + final long stamp = file.getModificationStamp(); + Document document = myDocumentManager.getDocument(file); + myDocumentManager.saveDocument(document); + assertEquals(stamp, file.getModificationStamp()); + } + + public void testSaveDocument_DocumentWasChanged() throws Exception { + final VirtualFile file = newTextFile(); + final long stamp = file.getModificationStamp(); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + + myDocumentManager.saveDocument(document); + assertTrue(stamp != file.getModificationStamp()); + assertEquals(document.getModificationStamp(), file.getModificationStamp()); + assertTrue(Arrays.equals("xxxtest".getBytes(), file.contentsToByteArray())); + } + + public void testSaveAllDocuments_DocumentWasChanged() throws Exception { + final VirtualFile file = newTextFile(); + final long stamp = file.getModificationStamp(); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + + myDocumentManager.saveAllDocuments(); + assertTrue(stamp != file.getModificationStamp()); + assertTrue(Arrays.equals("xxxtest".getBytes(), file.contentsToByteArray())); + } + + public void testGetFile() throws Exception { + final VirtualFile file = newTextFile(); + Document document = myDocumentManager.getDocument(file); + assertSame(file, myDocumentManager.getFile(document)); + } + + public void testConvertSeparators() throws Exception { + final VirtualFile file = new MockVirtualFile("test.txt", "test\rtest"); + Document document = myDocumentManager.getDocument(file); + assertEquals("test\ntest", document.getText()); + } + + public void testRememberSeparators() throws Exception { + final VirtualFile file = new MockVirtualFile("test.txt", "test\rtest"); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + myDocumentManager.saveAllDocuments(); + assertTrue(Arrays.equals("xxxtest\rtest".getBytes(), file.contentsToByteArray())); + } + + public void testContentChanged_noDocument() throws Exception { + final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest"); + file.setListener(myDocumentManager); + file.setContent(null, "xxx", true); + assertNull(myDocumentManager.getCachedDocument(file)); + } + + public void testContentChanged_documentPresent() throws Exception { + final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest"); + file.setListener(myDocumentManager); + Document document = myDocumentManager.getDocument(file); + file.setContent(null, "xxx", true); + assertEquals("xxx", document.getText()); + assertEquals(file.getModificationStamp(), document.getModificationStamp()); + } + + public void testContentChanged_ignoreEventsFromSelf() throws Exception { + final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest"); + file.setListener(myDocumentManager); + Document document = myDocumentManager.getDocument(file); + file.setContent(myDocumentManager, "xxx", true); + assertEquals("test\ntest", document.getText()); + } + + public void testContentChanged_ignoreEventsFromSelfOnSave() throws Exception { + final VirtualFile file = new MockVirtualFile("test.txt", "test\rtest") { + public Writer getWriter(final Object requestor, final long newModificationStamp) { + final VirtualFile self = this; + return new CharArrayWriter() { + @Override + public void close() { + super.close(); + long oldStamp = getModificationStamp(); + setModificationStamp(newModificationStamp); + setText(toString()); + myDocumentManager.contentsChanged(new VirtualFileEvent(requestor, self, null, oldStamp, getModificationStamp())); + } + }; + } + }; + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "xxx"); + final long stamp = document.getModificationStamp(); + + myDocumentManager.saveAllDocuments(); + assertEquals(stamp, document.getModificationStamp()); + } + + public void testContentChanged_reloadChangedDocument() throws Exception { + final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest"); + file.setListener(myDocumentManager); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "zzz"); + + myDocumentManager.myReloadFromDisk = Boolean.TRUE; + try { + file.setContent(null, "xxx", true); + + assertEquals("xxx", document.getText()); + assertEquals(file.getModificationStamp(), document.getModificationStamp()); + assertEquals(0, myDocumentManager.getUnsavedDocuments().length); + } + finally { + myDocumentManager.myReloadFromDisk = null; + } + } + + public void testContentChanged_dontreloadChangedDocument() throws Exception { + final MockVirtualFile file = newTextFile(); + file.setListener(myDocumentManager); + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "zzz"); + + myDocumentManager.myReloadFromDisk = Boolean.FALSE; + try { + long oldDocumentStamp = document.getModificationStamp(); + + file.setContent(null, "xxx", true); + + assertEquals("zzztest", document.getText()); + assertEquals(oldDocumentStamp, document.getModificationStamp()); + } + finally { + myDocumentManager.myReloadFromDisk = null; + } + } + + public void testSaveDocument_dontSaveIfModStampEqualsToFile() throws Exception { + final VirtualFile file = new MockVirtualFile("test.txt", "test") { + @NotNull + @Override + public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException { + fail(); + throw new IOException(); + } + }; + DocumentEx document = (DocumentEx)myDocumentManager.getDocument(file); + document.insertString(0, "zzz"); + document.setModificationStamp(file.getModificationStamp()); + + myDocumentManager.saveDocument(document); + } + + // this test requires changes in idea code to support MockFile as local file system file (FileDocumentManager.needsRefresh). + // TODO: think how to test this functionality without hacking production code + public void atestContentChanged_reloadChangedDocumentOnSave() throws Exception { + final MockVirtualFile file = new MockVirtualFile("test.txt", "test\rtest") { + @Override + public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) { + long oldStamp = getModificationStamp(); + setModificationStamp(LocalTimeCounter.currentTime()); + myDocumentManager.contentsChanged(new VirtualFileEvent(null, this, null, oldStamp, getModificationStamp())); + } + }; + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "zzz"); + file.setContent(null, "xxx", false); + + myDocumentManager.myReloadFromDisk = Boolean.TRUE; + try { + file.setActualTimeStamp(file.getTimeStamp() + 1); + myDocumentManager.saveAllDocuments(); + long fileStamp = file.getModificationStamp(); + + assertEquals("xxx", document.getText()); + assertEquals(file.getModificationStamp(), document.getModificationStamp()); + assertEquals(file.getModificationStamp(), fileStamp); + assertEquals(0, myDocumentManager.getUnsavedDocuments().length); + } + finally { + myDocumentManager.myReloadFromDisk = null; + } + } + + public void testContentChanged_dontreloadChangedDocumentOnSave() throws Exception { + final MockVirtualFile file = new MockVirtualFile("test.txt", "test") { + @Override + public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) { + long oldStamp = getModificationStamp(); + setModificationStamp(LocalTimeCounter.currentTime()); + myDocumentManager.contentsChanged(new VirtualFileEvent(null, this, null, oldStamp, getModificationStamp())); + } + }; + + myDocumentManager.myReloadFromDisk = Boolean.FALSE; + try { + Document document = myDocumentManager.getDocument(file); + document.insertString(0, "zzz"); + long documentStamp = document.getModificationStamp(); + + file.setContent(null, "xxx", false); + + file.setActualTimeStamp(file.getTimeStamp() + 1); + myDocumentManager.saveAllDocuments(); + + assertEquals("zzztest", document.getText()); + assertEquals(file.getModificationStamp(), document.getModificationStamp()); + assertTrue(Arrays.equals("zzztest".getBytes(), file.contentsToByteArray())); + assertEquals(documentStamp, document.getModificationStamp()); + } + finally { + myDocumentManager.myReloadFromDisk = null; + } + } + + public void testReplaceDocumentTestWithTheSameTest() throws Exception { + final boolean[] canCallSave = {true}; + final VirtualFile file = new MockVirtualFile("test.txt", "test") { + @NotNull + @Override + public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException { + if (!canCallSave[0]) fail(); + return super.getOutputStream(requestor, newModificationStamp, newTimeStamp); + } + }; + DocumentEx document = (DocumentEx)myDocumentManager.getDocument(file); + + String newText = "test text"; + document.replaceString(0, document.getTextLength(), newText); + assertTrue(myDocumentManager.isDocumentUnsaved(document)); + myDocumentManager.saveDocument(document); + + canCallSave[0] = false; + + final long modificationStamp = document.getModificationStamp(); + + document.replaceString(0, document.getTextLength(), newText); + + if (myDocumentManager.isDocumentUnsaved(document)) { + assertTrue(document.getModificationStamp() > modificationStamp); + } + else { + assertEquals(modificationStamp, document.getModificationStamp()); + } + } + + + public void testExternalReplaceWithTheSameText() throws Exception{ + final long[] modificationStamp = new long[1]; + + modificationStamp[0] = 1; + + final VirtualFile file = new MockVirtualFile("test.txt", "test") { + @NotNull + @Override + public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException { + fail(); + throw new IOException(); + } + + @Override + public long getModificationStamp() { + return modificationStamp[0]; + } + + @Override + public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) { + myDocumentManager.contentsChanged(new VirtualFileEvent(null, this, null, 1, 2)); + + } + }; + DocumentEx document = (DocumentEx)myDocumentManager.getDocument(file); + + + modificationStamp[0] = 2; + + file.refresh(false, false); + + assertEquals(2, document.getModificationStamp()); + } + + public static class MyFileDocumentManagerImpl extends FileDocumentManagerImpl { + + public static final FileDocumentManagerListener[] LISTENERS = new FileDocumentManagerListener[0]; + + public IOException myExceptionOnSave; + public Boolean myReloadFromDisk = null; + + public MyFileDocumentManagerImpl() { + super(new MockVirtualFileManager()); + } + + @Override + protected void reportErrorOnSave(IOException e) { + myExceptionOnSave = e; + } + + @Override + protected boolean askReloadFromDisk(VirtualFile file, Document document) { + if (myReloadFromDisk == null){ + fail(); + return false; + } + else{ + return myReloadFromDisk.booleanValue(); + } + } + + @NotNull + @Override + protected FileDocumentManagerListener[] getListeners() { + return LISTENERS; + } + } + + private static class MyMockCommandProcessor extends MockCommandProcessor { + @Override + public void executeCommand(Project project, @NotNull Runnable runnable, String name, Object groupId) { + runnable.run(); + } + + @Override + public void executeCommand(Project project, + @NotNull Runnable runnable, + String name, + Object groupId, + @NotNull UndoConfirmationPolicy confirmationPolicy, + Document document) { + runnable.run(); + } + + @Override + public void executeCommand(Project project, + @NotNull Runnable runnable, + String name, + Object groupId, + @NotNull UndoConfirmationPolicy confirmationPolicy) { + runnable.run(); + } + + @Override + public void executeCommand(@NotNull Runnable runnable, String name, Object groupId) { + runnable.run(); + } + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/IdeDocumentHistoryTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/IdeDocumentHistoryTest.java new file mode 100644 index 000000000000..04264ccb9df3 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/IdeDocumentHistoryTest.java @@ -0,0 +1,210 @@ +package com.intellij.openapi.fileEditor; + +import com.intellij.mock.Mock; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.testFramework.PlatformTestCase; +import org.jetbrains.annotations.NotNull; + +public class IdeDocumentHistoryTest extends PlatformTestCase { + private IdeDocumentHistoryImpl myHistory; + + private Mock.MyFileEditor mySelectedEditor; + private Mock.MyVirtualFile mySelectedFile; + private FileEditorState myEditorState; + private FileEditorProvider myProvider; + + private MyState myState1; + private MyState myState2; + private MyState myState3; + + @Override + protected void setUp() throws Exception { + super.setUp(); + myHistory = new IdeDocumentHistoryImpl(getProject(), EditorFactory.getInstance(), new EditorManager(), VirtualFileManager.getInstance(), CommandProcessor.getInstance(), new Mock.MyToolWindowManager()) { + @Override + protected Pair getSelectedEditor() { + return Pair.create ((FileEditor)mySelectedEditor, myProvider); + } + + @Override + protected void executeCommand(Runnable runnable, String name, Object groupId) { + myHistory.onCommandStarted(); + runnable.run(); + myHistory.onSelectionChanged(); + myHistory.onCommandFinished(groupId); + } + }; + + mySelectedEditor = new Mock.MyFileEditor() { + @Override + @NotNull + public FileEditorState getState(@NotNull FileEditorStateLevel level) { + return myEditorState; + } + + @Override + public void setState(@NotNull FileEditorState state) { + myEditorState = state; + } + }; + mySelectedFile = new Mock.MyVirtualFile(); + myEditorState = new MyState(false, "start"); + myProvider = new Mock.MyFileEditorProvider() { + @Override + @NotNull + public String getEditorTypeId() { + return "EditorType"; + } + }; + } + public void testNoHistoryRecording() throws Throwable { + myHistory.onCommandStarted(); + myHistory.onCommandFinished(null); + + assertFalse(myHistory.isBackAvailable()); + assertFalse(myHistory.isForwardAvailable()); + } + + public void testNavigationRecording() throws Throwable { + makeNavigationChange(myState2); + + assertTrue(myHistory.isBackAvailable()); + assertFalse(myHistory.isForwardAvailable()); + + assertEquals(1, myHistory.getBackPlaces().size()); + } + + public void testMergingForwardPlaces() throws Throwable { + myEditorState = new MyState(true, "state1"); + makeNavigationChange(new MyState(true, "state2")); + + assertTrue(myHistory.isBackAvailable()); + assertFalse(myHistory.isForwardAvailable()); + + assertEquals(1, myHistory.getBackPlaces().size()); + } + + public void testSimpleNavigation() throws Throwable { + pushTwoStates(); + + assertFalse(myHistory.isForwardAvailable()); + assertTrue(myHistory.isBackAvailable()); + + myHistory.back(); + assertTrue(myHistory.isBackAvailable()); + assertTrue(myHistory.isForwardAvailable()); + assertSame(myState2, myEditorState); + + myHistory.back(); + assertFalse(myHistory.isBackAvailable()); + assertTrue(myHistory.isForwardAvailable()); + assertSame(myState1, myEditorState); + + myHistory.forward(); + assertTrue(myHistory.isBackAvailable()); + assertTrue(myHistory.isForwardAvailable()); + assertSame(myState2, myEditorState); + + myHistory.forward(); + assertTrue(myHistory.isBackAvailable()); + assertFalse(myHistory.isForwardAvailable()); + assertSame(myState3, myEditorState); + } + + public void testQueueCutOff() throws Throwable { + pushTwoStates(); + myHistory.back(); + + assertTrue(myHistory.isBackAvailable()); + assertTrue(myHistory.isForwardAvailable()); + + MyState newState = new MyState(false, "newState"); + makeNavigationChange(newState); + + assertTrue(myHistory.isBackAvailable()); + assertFalse(myHistory.isForwardAvailable()); + + myHistory.back(); + assertSame(myState2, myEditorState); + myHistory.back(); + assertSame(myState1, myEditorState); + assertFalse(myHistory.isBackAvailable()); + } + + public void testRemoveInvalid() throws Throwable { + pushTwoStates(); + assertTrue(myHistory.isBackAvailable()); + + mySelectedFile.myValid = false; + + myHistory.onFileDeleted(); + + assertFalse(myHistory.isBackAvailable()); + assertFalse(myHistory.isForwardAvailable()); + } + + + private void pushTwoStates() { + myState1 = new MyState(false, "state1"); + myState2 = new MyState(false, "state2"); + myState3 = new MyState(false, "state3"); + + myEditorState = myState1; + makeNavigationChange(myState2); + makeNavigationChange(myState3); + } + + private void makeNavigationChange(MyState newState) { + myHistory.onCommandStarted(); + myHistory.onSelectionChanged(); + myHistory.onCommandFinished(null); + myEditorState = newState; + } + + private class EditorManager extends Mock.MyFileEditorManager { + + @Override + public VirtualFile getFile(@NotNull FileEditor editor) { + return mySelectedFile; + } + + @Override + @NotNull + public Pair openFileWithProviders(@NotNull VirtualFile file, + boolean focusEditor, + boolean searchForSplitter) { + return Pair.create (new FileEditor[] {mySelectedEditor}, new FileEditorProvider[] {myProvider}); + } + + @Override + public FileEditorProvider getProvider(FileEditor editor) { + return myProvider; + } + } + + private static class MyState implements FileEditorState { + + private final boolean myCanBeMerged; + private final String myName; + + public MyState(boolean canBeMerged, String name) { + myCanBeMerged = canBeMerged; + myName = name; + } + + @Override + public boolean canBeMergedWith(FileEditorState otherState, FileEditorStateLevel level) { + return myCanBeMerged; + } + + public String toString() { + return myName; + } + } + +} diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/LoadTextUtilTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/LoadTextUtilTest.java new file mode 100644 index 000000000000..74ead6c532b8 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/LoadTextUtilTest.java @@ -0,0 +1,37 @@ +package com.intellij.openapi.fileEditor; + +import com.intellij.openapi.fileEditor.impl.LoadTextUtil; +import com.intellij.openapi.util.Comparing; +import com.intellij.testFramework.LightPlatformTestCase; +import com.intellij.testFramework.LightVirtualFile; + +public class LoadTextUtilTest extends LightPlatformTestCase { + private static void doTest(String source, String expected, String expectedSeparator) { + final LightVirtualFile vFile = new LightVirtualFile("test.txt"); + final CharSequence real = LoadTextUtil.getTextByBinaryPresentation(source.getBytes(), vFile); + assertTrue("content", Comparing.equal(expected, real)); + if (expectedSeparator != null) { + assertEquals("detected line separator", expectedSeparator, FileDocumentManager.getInstance().getLineSeparator(vFile, null)); + } + } + + public void testSimpleLoad() { + doTest("test", "test", null); + } + + public void testConvert_SlashR() { + doTest("test\rtest\rtest", "test\ntest\ntest", "\r"); + } + + public void testConvert_SlashN() { + doTest("test\ntest\ntest", "test\ntest\ntest", "\n"); + } + + public void testConvert_SlashR_SlashN() { + doTest("test\r\ntest\r\ntest", "test\ntest\ntest", "\r\n"); + } + + public void testConvertMostCommon() { + doTest("test\r\ntest\r\ntest\ntest", "test\ntest\ntest\ntest", "\r\n"); + } +} diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerNodeDescriptor.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerNodeDescriptor.java index a5afce41398c..eccd92344e1f 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerNodeDescriptor.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerNodeDescriptor.java @@ -15,41 +15,18 @@ */ package com.intellij.execution.testframework.sm.runner; +import com.intellij.execution.testframework.ui.BaseTestProxyNodeDescriptor; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.project.Project; /** * @author: Roman Chernyatchik */ -public class SMTRunnerNodeDescriptor extends NodeDescriptor -{ - private final SMTestProxy myElement; - +public class SMTRunnerNodeDescriptor extends BaseTestProxyNodeDescriptor { public SMTRunnerNodeDescriptor(final Project project, - final SMTestProxy element, - final NodeDescriptor parentDesc) { - super(project, parentDesc); - myElement = element; - myName = element.getName(); - } - - @Override - public boolean update() { - return false; - } - - @Override - public SMTestProxy getElement() { - return myElement; - } - - public boolean expandOnDoubleClick() { - return !myElement.isLeaf(); - } - - @Override - public String toString() { - return myName; + final SMTestProxy testProxy, + final NodeDescriptor parentDesc) { + super(project, testProxy, parentDesc); } } diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerTreeBuilder.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerTreeBuilder.java index 95280b7fdd7c..e4d9ff5c9c08 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerTreeBuilder.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTRunnerTreeBuilder.java @@ -16,13 +16,10 @@ package com.intellij.execution.testframework.sm.runner; import com.intellij.execution.testframework.ui.AbstractTestTreeBuilder; +import com.intellij.ide.util.treeView.AbstractTreeStructure; +import com.intellij.ide.util.treeView.AbstractTreeUpdater; import com.intellij.ide.util.treeView.IndexComparator; import com.intellij.ide.util.treeView.NodeDescriptor; -import com.intellij.ide.util.treeView.AbstractTreeUpdater; -import com.intellij.ide.util.treeView.AbstractTreeStructure; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.util.StatusBarProgress; -import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; @@ -43,7 +40,7 @@ public class SMTRunnerTreeBuilder extends AbstractTestTreeBuilder { } public SMTRunnerTreeStructure getSMRunnerTreeStructure() { - return ((SMTRunnerTreeStructure)getTreeStructure()) ; + return ((SMTRunnerTreeStructure)getTreeStructure()); } public void updateTestsSubtree(final SMTestProxy parentTestProxy) { @@ -64,32 +61,16 @@ public class SMTRunnerTreeBuilder extends AbstractTestTreeBuilder { } if (((SMTestProxy)nodeElement).getParent() == rootElement - && ((SMTestProxy)rootElement).getChildren().size() == 1){ + && ((SMTestProxy)rootElement).getChildren().size() == 1) { return true; } return false; } - protected boolean isAlwaysShowPlus(final NodeDescriptor descriptor) { - //TODO[romeo] move to base class - return false; - } - - @NotNull - protected ProgressIndicator createProgressIndicator() { - //TODO[romeo] maybe move to base class - return new StatusBarProgress(); - } - - protected boolean isSmartExpand() { - //TODO[romeo] move to base class - return false; - } - /** * for java unit tests */ public void performUpdate() { getUpdater().performUpdate(); - } + } } diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java index 34f025b606ac..c2b5e04dc23c 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java @@ -54,7 +54,8 @@ import java.util.Set; /** * @author: Roman Chernyatchik */ -public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFrameworkRunningModel, TestResultsViewer, SMTRunnerEventsListener { +public class SMTestRunnerResultsForm extends TestResultsPanel + implements TestFrameworkRunningModel, TestResultsViewer, SMTRunnerEventsListener { @NonNls private static final String DEFAULT_SM_RUNNER_SPLITTER_PROPERTY = "SMTestRunner.Splitter.Proportion"; public static final Color DARK_YELLOW = Color.YELLOW.darker(); @@ -131,12 +132,12 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra final KeyStroke shiftEnterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_MASK); SMRunnerUtil.registerAsAction(shiftEnterKey, "show-statistics-for-test-proxy", - new Runnable() { - public void run() { - showStatisticsForSelectedProxy(); - } - }, - myTreeView); + new Runnable() { + public void run() { + showStatisticsForSelectedProxy(); + } + }, + myTreeView); } protected ToolbarPanel createToolbarPanel() { @@ -161,7 +162,9 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra final SMTRunnerTreeStructure structure = new SMTRunnerTreeStructure(myProject, myTestsRootNode); myTreeBuilder = new SMTRunnerTreeBuilder(myTreeView, structure); + myTreeBuilder.setTestsComparator(TestConsoleProperties.SORT_ALPHABETICALLY.value(myProperties)); Disposer.register(this, myTreeBuilder); + myAnimator = new MyAnimator(this, myTreeBuilder); //TODO always hide root node @@ -192,6 +195,7 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra /** * Is used for navigation from tree view to other UI components + * * @param handler */ public void setShowStatisticForProxyHandler(final PropagateSelectionHandler handler) { @@ -200,8 +204,9 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra /** * Returns root node, fake parent suite for all tests and suites - * @return + * * @param testsRoot + * @return */ public void onTestingStarted(@NotNull SMTestProxy.SMRootTestProxy testsRoot) { myAnimator.setCurrentTestCase(myTestsRootNode); @@ -333,6 +338,11 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra return myTreeView; } + @Override + public SMTRunnerTreeBuilder getTreeBuilder() { + return myTreeBuilder; + } + public boolean hasTestSuites() { return getRoot().getChildren().size() > 0; } @@ -345,10 +355,11 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra /** * Manual test proxy selection in tests tree. E.g. do select root node on * testing started or do select current node if TRACK_RUNNING_TEST is enabled - * - * + *

+ *

* Will select proxy in Event Dispatch Thread. Invocation of this * method may be not in event dispatch thread + * * @param testProxy Test or suite */ public void selectAndNotify(@Nullable final AbstractTestProxy testProxy) { @@ -469,7 +480,8 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra private void updateStatusLabel(final boolean testingFinished) { if (myTestsFailuresCount > 0) { myStatusLine.setStatusColor(ColorProgressBar.RED); - } else if (myContainsIgnoredTests) { + } + else if (myContainsIgnoredTests) { myStatusLine.setStatusColor(DARK_YELLOW); } @@ -477,7 +489,7 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra if (myTestsTotal == 0) { myStatusLine.setStatusColor(myTestsRootNode.wasLaunched() || !myTestsRootNode.isTestsReporterAttached() ? Color.LIGHT_GRAY - : ColorProgressBar.RED ); + : ColorProgressBar.RED); } // else color will be according failed/passed tests } @@ -486,9 +498,9 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra // initializing will be "launchedAndFinished" final boolean launchedAndFinished = myTestsRootNode.wasLaunched() && !myTestsRootNode.isInProgress(); myStatusLine.setText(TestsPresentationUtil.getProgressStatus_Text(myStartTime, myEndTime, - myTestsTotal, myTestsCurrentCount, - myTestsFailuresCount, myMentionedCategories, - launchedAndFinished)); + myTestsTotal, myTestsCurrentCount, + myTestsFailuresCount, myMentionedCategories, + launchedAndFinished)); } /** @@ -501,12 +513,13 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra /** * On event change selection and probably requests focus. Is used when we want * navigate from other component to this + * * @return Listener */ public PropagateSelectionHandler createSelectMeListener() { return new PropagateSelectionHandler() { public void handlePropagateSelectionRequest(@Nullable final SMTestProxy selectedTestProxy, @NotNull final Object sender, - final boolean requestFocus) { + final boolean requestFocus) { SMRunnerUtil.addToInvokeLater(new Runnable() { public void run() { selectWithoutNotify(selectedTestProxy); @@ -543,7 +556,8 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra if (!isModeConsistent(isCustomMessage)) return; // for mixed tests results : mention category only if it contained tests - myMentionedCategories.add(myCurrentCustomProgressCategory != null ? myCurrentCustomProgressCategory : TestsPresentationUtil.DEFAULT_TESTS_CATEGORY); + myMentionedCategories + .add(myCurrentCustomProgressCategory != null ? myCurrentCustomProgressCategory : TestsPresentationUtil.DEFAULT_TESTS_CATEGORY); // Counters myTestsCurrentCount++; @@ -558,7 +572,8 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra if (myTestsTotal != 0) { // if total is set myStatusLine.setFraction((double)myTestsCurrentCount / myTestsTotal); - } else { + } + else { // if at least one test was launcher than just set progress in the middle to show user that tests are running myStatusLine.setFraction(myTestsCurrentCount > 1 ? 0.5 : 0); // > 1 because count already ++ } @@ -583,34 +598,34 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra } - private static class MyFocusTraversalPolicy extends FocusTraversalPolicy { - final List myComponents; + private static class MyFocusTraversalPolicy extends FocusTraversalPolicy { + final List myComponents; - private MyFocusTraversalPolicy(final List components) { - myComponents = components; - } + private MyFocusTraversalPolicy(final List components) { + myComponents = components; + } - public Component getComponentAfter(final Container container, final Component component) { - return myComponents.get((myComponents.indexOf(component) + 1) % myComponents.size()); - } + public Component getComponentAfter(final Container container, final Component component) { + return myComponents.get((myComponents.indexOf(component) + 1) % myComponents.size()); + } - public Component getComponentBefore(final Container container, final Component component) { - final int prevIndex = myComponents.indexOf(component) - 1; - final int normalizedIndex = prevIndex < 0 ? myComponents.size() - 1 : prevIndex; + public Component getComponentBefore(final Container container, final Component component) { + final int prevIndex = myComponents.indexOf(component) - 1; + final int normalizedIndex = prevIndex < 0 ? myComponents.size() - 1 : prevIndex; - return myComponents.get(normalizedIndex); - } + return myComponents.get(normalizedIndex); + } - public Component getFirstComponent(final Container container) { - return myComponents.get(0); - } + public Component getFirstComponent(final Container container) { + return myComponents.get(0); + } - public Component getLastComponent(final Container container) { - return myComponents.get(myComponents.size() - 1); - } + public Component getLastComponent(final Container container) { + return myComponents.get(myComponents.size() - 1); + } - public Component getDefaultComponent(final Container container) { - return getFirstComponent(container); - } + public Component getDefaultComponent(final Container container) { + return getFirstComponent(container); + } } } diff --git a/platform/testFramework/src/com/intellij/mock/Mock.java b/platform/testFramework/src/com/intellij/mock/Mock.java new file mode 100644 index 000000000000..74c68d273eb0 --- /dev/null +++ b/platform/testFramework/src/com/intellij/mock/Mock.java @@ -0,0 +1,681 @@ +package com.intellij.mock; + +import com.intellij.codeHighlighting.BackgroundEditorHighlighter; +import com.intellij.ide.structureView.StructureViewBuilder; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.*; +import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; +import com.intellij.openapi.fileEditor.impl.EditorComposite; +import com.intellij.openapi.fileEditor.impl.EditorWindow; +import com.intellij.openapi.fileEditor.impl.EditorsSplitters; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.util.*; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileSystem; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowAnchor; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.impl.IdeFocusManagerHeadless; +import com.intellij.util.ArrayUtil; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.HyperlinkListener; +import java.awt.*; +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collections; +import java.util.List; + +//[kirillk] - this class looks to be an overkill but IdeDocumentHistory is highly coupled +// with all of that stuff below, so it's not possible to test it's back/forward capabilities +// w/o making mocks for all of them. perhaps later we will decouple those things +public class Mock { + + public static class MyFileEditor extends UserDataHolderBase implements DocumentsEditor { + public Document[] DOCUMENTS; + + @Override + public Document[] getDocuments() { + return DOCUMENTS; + } + + @Override + @NotNull + public JComponent getComponent() { + throw new UnsupportedOperationException(); + } + + @Override + public JComponent getPreferredFocusedComponent() { + return null; + } + + @Override + @NotNull + public String getName() { + return ""; + } + + @Override + public void dispose() { + } + + @Override + public StructureViewBuilder getStructureViewBuilder() { + return null; + } + + @Override + @NotNull + public FileEditorState getState(@NotNull FileEditorStateLevel level) { + return new FileEditorState() { + @Override + public boolean canBeMergedWith(FileEditorState fileEditorState, FileEditorStateLevel fileEditorStateLevel) { + return false; + } + }; + } + + @Override + public void setState(@NotNull FileEditorState state) { + } + + @Override + public boolean isModified() { + return false; + } + + @Override + public boolean isValid() { + return false; + } + + @Override + public void selectNotify() { + } + + @Override + public void deselectNotify() { + } + + @Override + public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) { + } + + @Override + public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) { + } + + @Override + public BackgroundEditorHighlighter getBackgroundHighlighter() { + return null; + } + + @Override + public FileEditorLocation getCurrentLocation() { + return null; + } + } + + public static class MyFileEditorManager extends FileEditorManagerEx { + @Override + public JComponent getComponent() { + return null; + } + + @Override + public ActionCallback notifyPublisher(Runnable runnable) { + runnable.run(); + return new ActionCallback.Done(); + } + + @Override + public ActionCallback getReady(@NotNull Object requestor) { + return new ActionCallback.Done(); + } + + @NotNull + @Override + public Pair openFileWithProviders(@NotNull VirtualFile file, + boolean focusEditor, + @NotNull EditorWindow window) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isInsideChange() { + return false; + } + + @Override + public boolean hasSplitOrUndockedWindows() { + return false; + } + + @Override + public EditorsSplitters getSplittersFor(Component c) { + return null; + } + + @Override + public EditorsSplitters getSplitters() { + return null; + } + + @Override + public AsyncResult getActiveWindow() { + return null; + } + + @Override + public void addTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { + } + + @Override + public void removeTopComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { + } + + @Override + public void addBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { + } + + @Override + public void removeBottomComponent(@NotNull final FileEditor editor, @NotNull final JComponent component) { + } + + @Override + public JComponent getPreferredFocusedComponent() { + return null; + } + + @Override + @NotNull + public Pair getEditorsWithProviders(@NotNull VirtualFile file) { + throw new UnsupportedOperationException(); + } + + public FileEditorProvider getProvider(FileEditor editor) { + return null; + } + + @Override + public EditorWindow getCurrentWindow() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void setCurrentWindow(EditorWindow window) { + } + + @Override + public VirtualFile getFile(@NotNull FileEditor editor) { + return null; + } + + @Override + public void updateFilePresentation(VirtualFile file) { + } + + @Override + public void unsplitWindow() { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void unsplitAllWindow() { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + @NotNull + public EditorWindow[] getWindows() { + return new EditorWindow[0]; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + @NotNull + public VirtualFile[] getSiblings(VirtualFile file) { + return new VirtualFile[0]; + } + + @Override + public void createSplitter(int orientation, @Nullable EditorWindow window) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void changeSplitterOrientation() { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void flipTabs() { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean tabsMode() { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isInSplitter() { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean hasOpenedFile() { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public VirtualFile getCurrentFile() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public Pair getSelectedEditorWithProvider(@NotNull VirtualFile file) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isChanged(@NotNull EditorComposite editor) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public EditorWindow getNextWindow(@NotNull EditorWindow window) { + return null; + } + + @Override + public EditorWindow getPrevWindow(@NotNull EditorWindow window) { + return null; + } + + @Override + public void closeAllFiles() { + } + + public Editor openTextEditorEnsureNoFocus(@NotNull OpenFileDescriptor descriptor) { + return null; + } + + @Override + @NotNull + public Pair openFileWithProviders(@NotNull VirtualFile file, + boolean focusEditor, + boolean searchForSplitter) { + return Pair.create (new FileEditor[0], new FileEditorProvider [0]); + } + + @Override + public void closeFile(@NotNull VirtualFile file) { + } + + @Override + public void closeFile(@NotNull VirtualFile file, @NotNull EditorWindow window) { + } + + @Override + public Editor openTextEditor(OpenFileDescriptor descriptor, boolean focusEditor) { + return null; + } + + @Override + public Editor getSelectedTextEditor() { + return null; + } + + @Override + public boolean isFileOpen(@NotNull VirtualFile file) { + return false; + } + + @Override + @NotNull + public VirtualFile[] getOpenFiles() { + return new VirtualFile[0]; + } + + @Override + @NotNull + public VirtualFile[] getSelectedFiles() { + return new VirtualFile[0]; + } + + @Override + @NotNull + public FileEditor[] getSelectedEditors() { + return new FileEditor[0]; + } + + @Override + public FileEditor getSelectedEditor(@NotNull VirtualFile file) { + return null; + } + + @Override + @NotNull + public FileEditor[] getEditors(@NotNull VirtualFile file) { + return new FileEditor[0]; + } + + @NotNull + @Override + public FileEditor[] getAllEditors(@NotNull VirtualFile file) { + return new FileEditor[0]; + } + + @Override + @NotNull + public FileEditor[] getAllEditors() { + return new FileEditor[0]; + } + + @Override + public void removeEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComoponent) { + } + + @Override + public void showEditorAnnotation(@NotNull FileEditor editor, @NotNull JComponent annotationComoponent) { + } + + @Override + public void addFileEditorManagerListener(@NotNull FileEditorManagerListener listener) { + } + + @Override + public void addFileEditorManagerListener(@NotNull FileEditorManagerListener listener, Disposable parentDisposable) { + } + + @Override + public void removeFileEditorManagerListener(@NotNull FileEditorManagerListener listener) { + } + + @Override + @NotNull + public List openEditor(@NotNull OpenFileDescriptor descriptor, boolean focusEditor) { + return Collections.emptyList(); + } + + @Override + @NotNull + public Project getProject() { + throw new UnsupportedOperationException(); + } + + @Override + public void registerExtraEditorDataProvider(@NotNull EditorDataProvider provider, Disposable parentDisposable) { + } + + @Override + public int getWindowSplitCount() { + return 0; + } + } + + public static class MyVirtualFile extends VirtualFile { + + public boolean myValid = true; + + @Override + @NotNull + public VirtualFileSystem getFileSystem() { + throw new UnsupportedOperationException(); + } + + @Override + public String getPath() { + return null; + } + + @Override + @NotNull + public String getName() { + throw new UnsupportedOperationException(); + } + + @Override + public void rename(Object requestor, @NotNull String newName) throws IOException { + } + + @Override + public boolean isWritable() { + return false; + } + + @Override + public boolean isDirectory() { + return false; + } + + @Override + public boolean isValid() { + return myValid; + } + + @Override + public VirtualFile getParent() { + return null; + } + + @Override + public VirtualFile[] getChildren() { + return new VirtualFile[0]; + } + + @Override + public VirtualFile createChildDirectory(Object requestor, String name) throws IOException { + return null; + } + + @Override + public VirtualFile createChildData(Object requestor, @NotNull String name) throws IOException { + return null; + } + + @Override + public void delete(Object requestor) throws IOException { + } + + @Override + public void move(Object requestor, @NotNull VirtualFile newParent) throws IOException { + } + + @Override + public InputStream getInputStream() throws IOException { + return null; + } + + @Override + @NotNull + public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + @NotNull + public byte[] contentsToByteArray() throws IOException { + return ArrayUtil.EMPTY_BYTE_ARRAY; + } + + @Override + public long getModificationStamp() { + return 0; + } + + @Override + public long getTimeStamp() { + return 0; + } + + @Override + public long getLength() { + return 0; + } + + @Override + public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) { + } + } + + public static class MyToolWindowManager extends ToolWindowManager { + @Override + public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @NotNull ToolWindowAnchor anchor) { + return null; + } + + @Override + public ToolWindow registerToolWindow(@NotNull String id, + @NotNull JComponent component, + @NotNull ToolWindowAnchor anchor, + Disposable parentDisposable, + boolean canWorkInDumbMode, boolean canCloseContents) { + return null; + } + + @Override + public ToolWindow registerToolWindow(@NotNull String id, + @NotNull JComponent component, + @NotNull ToolWindowAnchor anchor, + Disposable parentDisposable, + boolean canWorkInDumbMode) { + return null; + } + + @Override + public ToolWindow registerToolWindow(@NotNull String id, @NotNull JComponent component, @NotNull ToolWindowAnchor anchor, Disposable parentDisposable) { + return null; + } + + @Override + public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor) { + return null; + } + + @Override + public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor, + final Disposable parentDisposable, final boolean dumbAware) { + return null; + } + + @Override + public ToolWindow registerToolWindow(@NotNull final String id, final boolean canCloseContent, @NotNull final ToolWindowAnchor anchor, final boolean sideTool) { + return null; + } + + public JComponent getFocusTargetFor(final JComponent comp) { + return null; + } + + @Override + public void unregisterToolWindow(@NotNull String id) { + } + + @Override + public void activateEditorComponent() { + } + + public ActionCallback requestFocus(final Component c, final boolean forced) { + return new ActionCallback.Done(); + } + + public ActionCallback requestFocus(final ActiveRunnable command, final boolean forced) { + return new ActionCallback.Done(); + } + + @Override + public boolean isEditorComponentActive() { + return false; + } + + @Override + public String[] getToolWindowIds() { + return ArrayUtil.EMPTY_STRING_ARRAY; + } + + @Override + public String getActiveToolWindowId() { + return null; + } + + @Override + public ToolWindow getToolWindow(String id) { + return null; + } + + @Override + public void invokeLater(Runnable runnable) { + } + + @Override + public IdeFocusManager getFocusManager() { + return IdeFocusManagerHeadless.INSTANCE; + } + + @Override + public void notifyByBalloon(@NotNull final String toolWindowId, final MessageType type, @NotNull final String text, @Nullable final Icon icon, + @Nullable final HyperlinkListener listener) { + } + + @Override + public Balloon getToolWindowBalloon(String id) { + return null; + } + + @Override + public void notifyByBalloon(@NotNull final String toolWindowId, @NotNull final MessageType type, @NotNull final String htmlBody) { + } + } + + + public static class MyFileEditorProvider implements FileEditorProvider { + @Override + public boolean accept(@NotNull Project project, @NotNull VirtualFile file) { + return false; + } + + @Override + @NotNull + public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) { + throw new UnsupportedOperationException(); + } + + @Override + public void disposeEditor(@NotNull FileEditor editor) { + } + + @Override + @NotNull + public FileEditorState readState(@NotNull Element sourceElement, @NotNull Project project, @NotNull VirtualFile file) { + throw new UnsupportedOperationException(); + } + + @Override + public void writeState(@NotNull FileEditorState state, @NotNull Project project, @NotNull Element targetElement) { + } + + @Override + @NotNull + public String getEditorTypeId() { + throw new UnsupportedOperationException(); + } + + @Override + @NotNull + public FileEditorPolicy getPolicy() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/platform/testFramework/src/com/intellij/mock/MockCommandProcessor.java b/platform/testFramework/src/com/intellij/mock/MockCommandProcessor.java new file mode 100644 index 000000000000..981b97fc25df --- /dev/null +++ b/platform/testFramework/src/com/intellij/mock/MockCommandProcessor.java @@ -0,0 +1,104 @@ +package com.intellij.mock; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.command.CommandListener; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.command.UndoConfirmationPolicy; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class MockCommandProcessor extends CommandProcessor { + @Override + public void executeCommand(@NotNull Runnable runnable, String name, Object groupId) { + } + + @Override + public void executeCommand(Project project, @NotNull Runnable runnable, String name, Object groupId) { + } + + @Override + public void executeCommand(Project project, + @NotNull Runnable runnable, + String name, + Object groupId, + @NotNull UndoConfirmationPolicy confirmationPolicy) { + + } + + @Override + public void setCurrentCommandName(String name) { + } + + @Override + public void setCurrentCommandGroupId(Object groupId) { + } + + @Override + public Runnable getCurrentCommand() { + return null; + } + + @Override + public String getCurrentCommandName() { + return null; + } + + @Override + @Nullable + public Object getCurrentCommandGroupId() { + return null; + } + + @Override + public Project getCurrentCommandProject() { + return null; + } + + @Override + public void addCommandListener(@NotNull CommandListener listener) { + } + + @Override + public void addCommandListener(@NotNull CommandListener listener, @NotNull Disposable parentDisposable) { + } + + @Override + public void removeCommandListener(@NotNull CommandListener listener) { + } + + @Override + public boolean isUndoTransparentActionInProgress() { + return false; + } + + @Override + public void markCurrentCommandAsGlobal(Project project) { + + } + + @Override + public void runUndoTransparentAction(@NotNull Runnable action) { + } + + @Override + public void executeCommand(Project project, @NotNull Runnable command, String name, Object groupId, @NotNull UndoConfirmationPolicy confirmationPolicy, + Document document) { + } + + @Override + public void executeCommand(Project project, @NotNull Runnable runnable, @Nls String name, Object groupId, @Nullable Document document) { + + } + + @Override + public void addAffectedDocuments(Project project, @NotNull Document... docs) { + } + + @Override + public void addAffectedFiles(Project project, @NotNull VirtualFile... files) { + } +} diff --git a/platform/testFramework/src/com/intellij/mock/MockVirtualFile.java b/platform/testFramework/src/com/intellij/mock/MockVirtualFile.java index 5478d0b84890..4a8ac05d43ff 100644 --- a/platform/testFramework/src/com/intellij/mock/MockVirtualFile.java +++ b/platform/testFramework/src/com/intellij/mock/MockVirtualFile.java @@ -3,15 +3,14 @@ */ package com.intellij.mock; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileSystem; +import com.intellij.openapi.vfs.*; +import com.intellij.util.LocalTimeCounter; import com.intellij.util.SmartList; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -26,8 +25,8 @@ public class MockVirtualFile extends VirtualFile { private final boolean myDirectory; private final List myChildren = new SmartList(); private String myText; - private FileType myFileType; private final MockVirtualFileSystem myFileSystem = new MockVirtualFileSystem(); + private long myModStamp = LocalTimeCounter.currentTime(); public MockVirtualFile(final String name) { this(false, name); @@ -38,6 +37,12 @@ public class MockVirtualFile extends VirtualFile { myName = name; } + public MockVirtualFile(String name, String text) { + myName = name; + myText = text; + myDirectory = false; + } + public void setText(final String text) { myText = text; } @@ -73,9 +78,13 @@ public class MockVirtualFile extends VirtualFile { return prefix + "/" + myName; } + private boolean myIsWritable = true; @Override public boolean isWritable() { - throw new UnsupportedOperationException("Method isWritable is not yet implemented in " + getClass().getName()); + return myIsWritable; + } + public void setWritable(boolean b) { + myIsWritable = b; } @Override @@ -96,13 +105,28 @@ public class MockVirtualFile extends VirtualFile { @Override public VirtualFile[] getChildren() { - return (VirtualFile[])VfsUtil.toVirtualFileArray(myChildren); + return VfsUtil.toVirtualFileArray(myChildren); } @Override @NotNull - public OutputStream getOutputStream(Object requestor, long newModificationStamp, long newTimeStamp) throws IOException { - throw new UnsupportedOperationException("Method getOutputStream is not yet implemented in " + getClass().getName()); + public OutputStream getOutputStream(Object requestor, final long newModificationStamp, long newTimeStamp) throws IOException { + return new ByteArrayOutputStream() { + @Override + public void close() { + myModStamp = newModificationStamp; + myText = toString(); + } + }; + } + + @Override + public long getModificationStamp() { + return myModStamp; + } + + public void setModificationStamp(long modStamp) { + myModStamp = modStamp; } @Override @@ -111,9 +135,11 @@ public class MockVirtualFile extends VirtualFile { return myText.getBytes(); } + + private final long myTimeStamp = System.currentTimeMillis(); @Override public long getTimeStamp() { - throw new UnsupportedOperationException("Method getTimeStamp is not yet implemented in " + getClass().getName()); + return myTimeStamp; } @Override @@ -123,7 +149,16 @@ public class MockVirtualFile extends VirtualFile { @Override public void refresh(boolean asynchronous, boolean recursive, Runnable postRunnable) { - throw new UnsupportedOperationException("Method refresh is not yet implemented in " + getClass().getName()); + + } + + private long myActualTimeStamp = myTimeStamp; + public void setActualTimeStamp(long actualTimeStamp) { + myActualTimeStamp = actualTimeStamp; + } + + public long getActualTimeStamp() { + return myActualTimeStamp; } @Override @@ -131,6 +166,19 @@ public class MockVirtualFile extends VirtualFile { throw new UnsupportedOperationException("Method getInputStream is not yet implemented in " + getClass().getName()); } + private VirtualFileListener myListener = null; + public void setListener(VirtualFileListener listener) { + myListener = listener; + } + + public void setContent(Object requestor, String content, boolean fireEvent) { + long oldStamp = myModStamp; + myText = content; + if (fireEvent) { + myModStamp = LocalTimeCounter.currentTime(); + myListener.contentsChanged(new VirtualFileEvent(requestor, this, null, oldStamp, myModStamp)); + } + } } diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java index 8ee4505c515a..d575d7df5019 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java @@ -145,10 +145,10 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro if (resource == null) { resource = PlatformTestCase.class.getClassLoader().getResource("idea/IdeaApplicationInfo.xml"); if (resource == null) { - System.setProperty("idea.platform.prefix", "PlatformLangXml"); + setPlatformPrefix("PlatformLangXml"); } else { - System.setProperty("idea.platform.prefix", "Idea"); + setPlatformPrefix("Idea"); } } } @@ -742,11 +742,15 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro isUltimate = false; } if (!isUltimate) { - System.setProperty("idea.platform.prefix", prefix); + setPlatformPrefix(prefix); } } } + public static void setPlatformPrefix(String prefix) { + System.setProperty("idea.platform.prefix", prefix); + } + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface WrapInCommand {} diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java index 57332b78d476..fa670d6c4308 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java @@ -41,6 +41,7 @@ import java.util.HashMap; public abstract class TestConsoleProperties extends StoringPropertyContainer implements Disposable { public static final BooleanProperty SCROLL_TO_STACK_TRACE = new BooleanProperty("scrollToStackTrace", false); + public static final BooleanProperty SORT_ALPHABETICALLY = new BooleanProperty("sortTestsAlphabetically", false); public static final BooleanProperty SELECT_FIRST_DEFECT = new BooleanProperty("selectFirtsDefect", false); public static final BooleanProperty TRACK_RUNNING_TEST = new BooleanProperty("trackRunningTest", true); public static final BooleanProperty HIDE_PASSED_TESTS = new BooleanProperty("hidePassedTests", true); @@ -69,7 +70,7 @@ public abstract class TestConsoleProperties extends StoringPropertyContainer imp public GlobalSearchScope getScope() { Module[] modules = getConfiguration().getModules(); if (modules.length == 0) return GlobalSearchScope.allScope(myProject); - + GlobalSearchScope scope = GlobalSearchScope.EMPTY_SCOPE; for (Module each : modules) { scope = scope.uniteWith(GlobalSearchScope.moduleRuntimeScope(each, true)); @@ -135,12 +136,12 @@ public abstract class TestConsoleProperties extends StoringPropertyContainer imp /** * Allows to make console editable and disable/enable input sending in process stdin stream. * Normally tests shouldn't ask anything in stdin so console is view only by default. - * + *

* NB1: Process input support feature isn't fully implemented. Input text will be lost after * switching to any other test/suite in tests results view. It's highly not recommended to change * default behaviour. Please do it only in critical cases and only if you are sure that you need this feature. - * - * + *

+ *

* NB2: If you are using Service Messages based test runner please ensure that before each service message * (e.g. #teamcity[...]) you always send "\n" to the output stream. * diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestFrameworkRunningModel.java b/platform/testRunner/src/com/intellij/execution/testframework/TestFrameworkRunningModel.java index a018a613fc41..9080d7f466fc 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestFrameworkRunningModel.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestFrameworkRunningModel.java @@ -20,20 +20,23 @@ */ package com.intellij.execution.testframework; +import com.intellij.execution.testframework.ui.AbstractTestTreeBuilder; import com.intellij.openapi.Disposable; public interface TestFrameworkRunningModel extends Disposable { TestConsoleProperties getProperties(); - void setFilter(final Filter filter); + void setFilter(Filter filter); boolean isRunning(); TestTreeView getTreeView(); + AbstractTestTreeBuilder getTreeBuilder(); + boolean hasTestSuites(); AbstractTestProxy getRoot(); - void selectAndNotify(final AbstractTestProxy testProxy); + void selectAndNotify(AbstractTestProxy testProxy); } \ No newline at end of file diff --git a/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java b/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java index c8672fca7366..9538c13bd891 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java @@ -28,6 +28,7 @@ import com.intellij.execution.testframework.actions.ShowStatisticsAction; import com.intellij.execution.testframework.actions.TestFrameworkActions; import com.intellij.execution.testframework.actions.TestTreeExpander; import com.intellij.execution.testframework.export.ExportTestResultsAction; +import com.intellij.execution.testframework.ui.AbstractTestTreeBuilder; import com.intellij.ide.CommonActionsManager; import com.intellij.ide.OccurenceNavigator; import com.intellij.openapi.Disposable; @@ -54,19 +55,25 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab public ToolbarPanel(final TestConsoleProperties properties, final RunnerSettings runnerSettings, final ConfigurationPerRunnerSettings configurationSettings, JComponent parent) { - super (new BorderLayout()); + super(new BorderLayout()); final DefaultActionGroup actionGroup = new DefaultActionGroup(null, false); actionGroup.addAction(new ToggleBooleanProperty(ExecutionBundle.message("junit.run.hide.passed.action.name"), ExecutionBundle.message("junit.run.hide.passed.action.description"), TestsUIUtil.loadIcon("hidePassed"), properties, TestConsoleProperties.HIDE_PASSED_TESTS)); actionGroup.addSeparator(); - + actionGroup.addAction(new ToggleBooleanProperty(ExecutionBundle.message("junit.runing.info.track.test.action.name"), ExecutionBundle.message("junit.runing.info.track.test.action.description"), TestsUIUtil.loadIcon("trackTests"), properties, TestConsoleProperties.TRACK_RUNNING_TEST)).setAsSecondary(true); + actionGroup.addAction(new ToggleBooleanProperty(ExecutionBundle.message("junit.runing.info.sort.alphabetically.action.name"), + ExecutionBundle.message("junit.runing.info.sort.alphabetically.action.description"), + IconLoader.getIcon("/objectBrowser/sorted.png"), + properties, TestConsoleProperties.SORT_ALPHABETICALLY)); + actionGroup.addSeparator(); + AnAction action = CommonActionsManager.getInstance().createCollapseAllAction(myTreeExpander, parent); action.getTemplatePresentation().setDescription(ExecutionBundle.message("junit.runing.info.collapse.test.action.name")); actionGroup.add(action); @@ -92,7 +99,8 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab myScrollToSource = new ScrollToTestSourceAction(properties); actionGroup.addAction(myScrollToSource).setAsSecondary(true); actionGroup.addAction(new ToggleBooleanProperty(ExecutionBundle.message("junit.runing.info.open.source.at.exception.action.name"), - ExecutionBundle.message("junit.runing.info.open.source.at.exception.action.description"), + ExecutionBundle + .message("junit.runing.info.open.source.at.exception.action.description"), IconLoader.getIcon("/runConfigurations/sourceAtException.png"), properties, TestConsoleProperties.OPEN_FAILURE_LINE)).setAsSecondary(true); @@ -110,8 +118,8 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab appendAdditionalActions(actionGroup, properties, runnerSettings, configurationSettings, parent); add(ActionManager.getInstance(). - createActionToolbar(ActionPlaces.TESTTREE_VIEW_TOOLBAR, actionGroup, true). - getComponent(), BorderLayout.CENTER); + createActionToolbar(ActionPlaces.TESTTREE_VIEW_TOOLBAR, actionGroup, true). + getComponent(), BorderLayout.CENTER); } protected void appendAdditionalActions(DefaultActionGroup actionGroup, TestConsoleProperties properties, RunnerSettings runnerSettings, @@ -128,6 +136,15 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab for (ToggleModelAction action : myActions) { action.setModel(model); } + TestFrameworkActions.addPropertyListener(TestConsoleProperties.SORT_ALPHABETICALLY, new TestFrameworkPropertyListener() { + @Override + public void onChanged(Boolean value) { + final AbstractTestTreeBuilder builder = model.getTreeBuilder(); + if (builder != null) { + builder.setTestsComparator(value); + } + } + }, model, true); } public boolean hasNextOccurence() { @@ -158,5 +175,4 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab myScrollToSource.setModel(null); myExportAction.setModel(null); } - } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/ui/AbstractTestTreeBuilder.java b/platform/testRunner/src/com/intellij/execution/testframework/ui/AbstractTestTreeBuilder.java index 94a41caadb32..4db467ae7c7f 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/ui/AbstractTestTreeBuilder.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/ui/AbstractTestTreeBuilder.java @@ -16,9 +16,10 @@ package com.intellij.execution.testframework.ui; import com.intellij.execution.testframework.AbstractTestProxy; -import com.intellij.ide.util.treeView.AbstractTreeBuilder; -import com.intellij.ide.util.treeView.AbstractTreeStructure; -import com.intellij.ide.util.treeView.IndexComparator; +import com.intellij.ide.util.treeView.*; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.util.StatusBarProgress; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; @@ -51,4 +52,22 @@ public abstract class AbstractTestTreeBuilder extends AbstractTreeBuilder { } while (current != null); } + + protected boolean isAlwaysShowPlus(final NodeDescriptor descriptor) { + return false; + } + + @NotNull + protected ProgressIndicator createProgressIndicator() { + return new StatusBarProgress(); + } + + protected boolean isSmartExpand() { + return false; + } + + public void setTestsComparator(boolean sortAlphabetically) { + setNodeDescriptorComparator(sortAlphabetically ? AlphaComparator.INSTANCE : null); + queueUpdate(); + } } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/ui/BaseTestProxyNodeDescriptor.java b/platform/testRunner/src/com/intellij/execution/testframework/ui/BaseTestProxyNodeDescriptor.java new file mode 100644 index 000000000000..80c979a4f4e4 --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/ui/BaseTestProxyNodeDescriptor.java @@ -0,0 +1,65 @@ +/* + * 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.execution.testframework.ui; + +import com.intellij.execution.testframework.AbstractTestProxy; +import com.intellij.ide.util.treeView.NodeDescriptor; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Roman.Chernyatchik + */ +public class BaseTestProxyNodeDescriptor extends NodeDescriptor { + @NotNull private final T myTestProxy; + + public BaseTestProxyNodeDescriptor(@Nullable final Project project, + @NotNull final T testProxy, + @Nullable final NodeDescriptor parentDescriptor) { + super(project, parentDescriptor); + myTestProxy = testProxy; + myName = testProxy.getName(); + } + + @Override + public int getWeight() { + return myTestProxy.isLeaf() ? 10 : 5; + } + + public String getName() { + return myTestProxy.getName(); + } + + public boolean expandOnDoubleClick() { + return !getElement().isLeaf(); + } + + @Override + public boolean update() { + return false; + } + + @Override + public T getElement() { + return myTestProxy; + } + + @Override + public String toString() { + return myName; + } +} diff --git a/platform/util/src/com/intellij/AbstractBundle.java b/platform/util/src/com/intellij/AbstractBundle.java index 9230d95e1776..0629b8f0c2f6 100644 --- a/platform/util/src/com/intellij/AbstractBundle.java +++ b/platform/util/src/com/intellij/AbstractBundle.java @@ -21,6 +21,7 @@ import com.intellij.util.containers.FactoryMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.util.Locale; import java.util.ResourceBundle; @@ -43,6 +44,7 @@ import java.util.ResourceBundle; * @since 8/1/11 2:37 PM */ public abstract class AbstractBundle { + private Reference myBundle; @NonNls private final String myPathToBundle; protected AbstractBundle(@NonNls @NotNull String pathToBundle) { @@ -54,7 +56,13 @@ public abstract class AbstractBundle { } private ResourceBundle getBundle() { - return getResourceBundle(myPathToBundle, getClass().getClassLoader()); + ResourceBundle bundle = null; + if (myBundle != null) bundle = myBundle.get(); + if (bundle == null) { + bundle = getResourceBundle(myPathToBundle, getClass().getClassLoader()); + myBundle = new SoftReference(bundle); + } + return bundle; } @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") diff --git a/platform/util/src/com/intellij/openapi/util/io/BufferExposingByteArrayInputStream.java b/platform/util/src/com/intellij/openapi/util/io/BufferExposingByteArrayInputStream.java index d0c9412efe07..9a2efb3e2ba7 100644 --- a/platform/util/src/com/intellij/openapi/util/io/BufferExposingByteArrayInputStream.java +++ b/platform/util/src/com/intellij/openapi/util/io/BufferExposingByteArrayInputStream.java @@ -15,17 +15,16 @@ */ package com.intellij.openapi.util.io; +import com.intellij.util.io.UnsyncByteArrayInputStream; import org.jetbrains.annotations.NotNull; -import java.io.ByteArrayInputStream; - -public class BufferExposingByteArrayInputStream extends ByteArrayInputStream { +public class BufferExposingByteArrayInputStream extends UnsyncByteArrayInputStream { public BufferExposingByteArrayInputStream(@NotNull byte[] bytes) { super(bytes); } @NotNull public byte[] getInternalBuffer() { - return buf; + return myBuffer; } } diff --git a/platform/util/src/com/intellij/openapi/util/io/BufferExposingByteArrayOutputStream.java b/platform/util/src/com/intellij/openapi/util/io/BufferExposingByteArrayOutputStream.java index 61fe2ea73020..990088c1842f 100644 --- a/platform/util/src/com/intellij/openapi/util/io/BufferExposingByteArrayOutputStream.java +++ b/platform/util/src/com/intellij/openapi/util/io/BufferExposingByteArrayOutputStream.java @@ -15,13 +15,11 @@ */ package com.intellij.openapi.util.io; +import com.intellij.util.io.UnsyncByteArrayOutputStream; import org.jetbrains.annotations.NotNull; -import java.io.ByteArrayOutputStream; - -public class BufferExposingByteArrayOutputStream extends ByteArrayOutputStream { - public BufferExposingByteArrayOutputStream() { - } +public class BufferExposingByteArrayOutputStream extends UnsyncByteArrayOutputStream { + public BufferExposingByteArrayOutputStream() {} public BufferExposingByteArrayOutputStream(int size) { super(size); @@ -29,14 +27,14 @@ public class BufferExposingByteArrayOutputStream extends ByteArrayOutputStream { @NotNull public byte[] getInternalBuffer() { - return buf; + return myBuffer; } // moves back the written bytes pointer by {@link #size}, to "unwrite" last {@link #size} bytes public int backOff(int size) { assert size >= 0 : size; - count -= size; - assert count >= 0 : count; - return count; + myCount -= size; + assert myCount >= 0 : myCount; + return myCount; } } diff --git a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java index c56c28e89536..d46a663babf1 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -22,11 +22,14 @@ import com.sun.jna.Library; import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.Pointer; +import com.sun.org.apache.bcel.internal.generic.NEW; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.io.IOException; import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; @@ -73,9 +76,9 @@ public class FileSystemUtil { private FileSystemUtil() { } - public static boolean isSymLink(@NotNull final File file) { + public static boolean isSymLink(@NotNull final String path) { try { - return ourMediator != null && file.exists() && ourMediator.isSymLink(file.getAbsolutePath()); + return ourMediator != null && ourMediator.isSymLink(path); } catch (Exception e) { LOG.warn(e); @@ -83,15 +86,15 @@ public class FileSystemUtil { } } - public static boolean isSymLink(@NotNull final String path) { - return isSymLink(new File(path)); + public static boolean isSymLink(@NotNull final File file) { + return isSymLink(file.getAbsolutePath()); } @Nullable - public static String resolveSymLink(@NotNull final File file) { - if (file.exists() && ourMediator != null) { + public static String resolveSymLink(@NotNull final String path) { + if (ourMediator != null) { try { - final String realPath = ourMediator.resolveSymLink(file.getAbsolutePath()); + final String realPath = ourMediator.resolveSymLink(path); if (realPath != null && new File(realPath).exists()) { return realPath; } @@ -104,18 +107,14 @@ public class FileSystemUtil { } @Nullable - public static String resolveSymLink(@NotNull final String path) { - return resolveSymLink(new File(path)); + public static String resolveSymLink(@NotNull final File file) { + return resolveSymLink(file.getAbsolutePath()); } public static int getPermissions(@NotNull final String path) { - return getPermissions(new File(path)); - } - - public static int getPermissions(@NotNull final File file) { - if (file.exists() && SystemInfo.isUnix && ourMediator != null) { + if (SystemInfo.isUnix && ourMediator != null) { try { - return ourMediator.getPermissions(file.getAbsolutePath()); + return ourMediator.getPermissions(path); } catch (Exception e) { LOG.warn(e); @@ -124,14 +123,14 @@ public class FileSystemUtil { return -1; } - public static void setPermissions(@NotNull final String path, final int permissions) { - setPermissions(new File(path), permissions); + public static int getPermissions(@NotNull final File file) { + return getPermissions(file.getAbsolutePath()); } - public static void setPermissions(@NotNull final File file, final int permissions) { - if (file.exists() && SystemInfo.isUnix && ourMediator != null) { + public static void setPermissions(@NotNull final String path, final int permissions) { + if (SystemInfo.isUnix && ourMediator != null) { try { - ourMediator.setPermissions(file.getAbsolutePath(), permissions); + ourMediator.setPermissions(path, permissions); } catch (Exception e) { LOG.warn(e); @@ -139,6 +138,10 @@ public class FileSystemUtil { } } + public static void setPermissions(@NotNull final File file, final int permissions) { + setPermissions(file.getAbsolutePath(), permissions); + } + private interface Mediator { boolean isSymLink(@NotNull String path) throws Exception; @@ -156,20 +159,28 @@ public class FileSystemUtil { private final Method myGetPath; private final Method myIsSymbolicLink; private final Object myLinkOptions; + private final Object myNoFollowLinkOptions; private final Method myGetAttribute; private final Method mySetAttribute; private Jdk7MediatorImpl() throws Exception { myDefaultFileSystem = Class.forName("java.nio.file.FileSystems").getMethod("getDefault").invoke(null); + myGetPath = Class.forName("java.nio.file.FileSystem").getMethod("getPath", String.class, String[].class); myGetPath.setAccessible(true); + final Class pathClass = Class.forName("java.nio.file.Path"); myIsSymbolicLink = Class.forName("java.nio.file.Files").getMethod("isSymbolicLink", pathClass); myIsSymbolicLink.setAccessible(true); - myLinkOptions = Array.newInstance(Class.forName("java.nio.file.LinkOption"), 0); - final Class linkOptClass = myLinkOptions.getClass(); - myGetAttribute = Class.forName("java.nio.file.Files").getMethod("getAttribute", pathClass, String.class, linkOptClass); - mySetAttribute = Class.forName("java.nio.file.Files").getMethod("setAttribute", pathClass, String.class, Object.class, linkOptClass); + + final Class linkOptClass = Class.forName("java.nio.file.LinkOption"); + myLinkOptions = Array.newInstance(linkOptClass, 0); + myNoFollowLinkOptions = Array.newInstance(linkOptClass, 1); + Array.set(myNoFollowLinkOptions, 0, linkOptClass.getField("NOFOLLOW_LINKS").get(null)); + + final Class linkOptArrClass = myLinkOptions.getClass(); + myGetAttribute = Class.forName("java.nio.file.Files").getMethod("getAttribute", pathClass, String.class, linkOptArrClass); + mySetAttribute = Class.forName("java.nio.file.Files").getMethod("setAttribute", pathClass, String.class, Object.class, linkOptArrClass); } @Override @@ -180,6 +191,7 @@ public class FileSystemUtil { @Override public String resolveSymLink(@NotNull final String path) throws Exception { + if (!new File(path).exists()) return null; final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY); final Method toRealPath = pathObj.getClass().getMethod("toRealPath", myLinkOptions.getClass()); toRealPath.setAccessible(true); @@ -189,7 +201,7 @@ public class FileSystemUtil { @Override public int getPermissions(@NotNull final String path) throws Exception { final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY); - final Object attribute = myGetAttribute.invoke(null, pathObj, POSIX_PERMISSIONS_ATTR, myLinkOptions); + final Object attribute = myGetAttribute.invoke(null, pathObj, POSIX_PERMISSIONS_ATTR, myNoFollowLinkOptions); return decodePermissions(attribute); } 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 a47df50805c8..3ed50898fc01 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * 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. @@ -678,9 +678,10 @@ public class FileUtil { } if (SystemInfo.isUnix && fromFile.canExecute()) { - final int permissions = FileSystemUtil.getPermissions(fromFile); - if (permissions != -1) { - FileSystemUtil.setPermissions(toFile, permissions); + final int oldPermissions = FileSystemUtil.getPermissions(fromFile); + final int newPermissions = FileSystemUtil.getPermissions(toFile); + if (oldPermissions != -1 && newPermissions != -1) { + FileSystemUtil.setPermissions(toFile, (oldPermissions | newPermissions)); } } } diff --git a/platform/util/src/com/intellij/util/io/IntToIntBtree.java b/platform/util/src/com/intellij/util/io/IntToIntBtree.java index 8df25adaa3b8..5c73f192038d 100644 --- a/platform/util/src/com/intellij/util/io/IntToIntBtree.java +++ b/platform/util/src/com/intellij/util/io/IntToIntBtree.java @@ -2,7 +2,6 @@ package com.intellij.util.io; import com.intellij.openapi.util.io.FileUtil; import gnu.trove.TIntIntHashMap; -import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; @@ -130,10 +129,11 @@ class IntToIntBtree { return pageStart; } - public @Nullable Integer get(int key) { + public boolean get(int key, int[] result) { if (hasCachedMappings) { if (myCachedMappings.containsKey(key)) { - return myCachedMappings.get(key); + result[0] = myCachedMappings.get(key); + return true; } } @@ -141,8 +141,9 @@ class IntToIntBtree { currentIndexNode.setAddress(root.address); int index = currentIndexNode.locate(key, false); - if (index < 0) return null; - return currentIndexNode.addressAt(index); + if (index < 0) return false; + result[0] = currentIndexNode.addressAt(index); + return true; } public void put(int key, int value) { diff --git a/platform/util/src/com/intellij/util/io/PersistentBTreeEnumerator.java b/platform/util/src/com/intellij/util/io/PersistentBTreeEnumerator.java index f99784a9d79d..5c21065d3a33 100644 --- a/platform/util/src/com/intellij/util/io/PersistentBTreeEnumerator.java +++ b/platform/util/src/com/intellij/util/io/PersistentBTreeEnumerator.java @@ -186,9 +186,11 @@ public class PersistentBTreeEnumerator extends PersistentEnumeratorBase 0) { if (!p.process(record)) return false; } else { @@ -259,22 +261,23 @@ public class PersistentBTreeEnumerator extends PersistentEnumeratorBase 0) { @@ -309,7 +312,7 @@ public class PersistentBTreeEnumerator extends PersistentEnumeratorBase extends PersistentEnumeratorDelegate< super(new BufferExposingByteArrayOutputStream()); } - public int getBufferSize() { - return ((ByteArrayOutputStream)out).size(); + private void reset() { + ((UnsyncByteArrayOutputStream)out).reset(); } - public void writeTo(OutputStream stream) throws IOException { - ((ByteArrayOutputStream)out).writeTo(stream); - } - - public void reset() { - ((ByteArrayOutputStream)out).reset(); - } - - public byte[] toByteArray() { - return ((ByteArrayOutputStream)out).toByteArray(); - } - - public ByteSequence getInternalBuffer() { - final BufferExposingByteArrayOutputStream _out = (BufferExposingByteArrayOutputStream)out; - return new ByteSequence(_out.getInternalBuffer(), 0, _out.size()); + private BufferExposingByteArrayOutputStream getInternalBuffer() { + return (BufferExposingByteArrayOutputStream)out; } } @@ -110,16 +97,14 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< protected void onDropFromCache(final Key key, final AppendStream value) { synchronized (PersistentEnumerator.ourLock) { try { - final ByteSequence bytes = value.getInternalBuffer(); + final BufferExposingByteArrayOutputStream bytes = value.getInternalBuffer(); final int id = enumerate(key); - HeaderRecord oldHeaderRecord = readValueId(id); + long oldHeaderRecord = readValueId(id); - HeaderRecord headerRecord = new HeaderRecord( - myValueStorage.appendBytes(bytes, oldHeaderRecord.address) - ); + long headerRecord = myValueStorage.appendBytes(bytes.getInternalBuffer(), 0, bytes.size(), oldHeaderRecord); updateValueId(id, headerRecord, oldHeaderRecord, key, 0); - if (oldHeaderRecord == HeaderRecord.EMPTY) { + if (oldHeaderRecord == NULL_ADDR) { myLiveAndGarbageKeysCounter += LIVE_KEY_MASK; } @@ -266,18 +251,18 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< final AppendStream record = new AppendStream(); myValueExternalizer.save(record, value); - final ByteSequence bytes = record.getInternalBuffer(); + final BufferExposingByteArrayOutputStream bytes = record.getInternalBuffer(); final int id = enumerate(key); - HeaderRecord oldheader = readValueId(id); - if (oldheader != HeaderRecord.EMPTY) { + long oldheader = readValueId(id); + if (oldheader != NULL_ADDR) { myLiveAndGarbageKeysCounter++; } else { myLiveAndGarbageKeysCounter += LIVE_KEY_MASK; } - HeaderRecord header = new HeaderRecord(myValueStorage.appendBytes(bytes, 0)); + long header = myValueStorage.appendBytes(bytes.getInternalBuffer(), 0, bytes.size(), 0); updateValueId(id, header, oldheader, key, 0); } @@ -330,41 +315,39 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< myAppendCache.clear(); return myEnumerator.processAllDataObject(processor, new PersistentEnumerator.DataFilter() { public boolean accept(final int id) { - return readValueId(id).address != NULL_ADDR; + return readValueId(id) != NULL_ADDR; } }); } } - public final Value get(Key key) throws IOException { + public final @Nullable Value get(Key key) throws IOException { synchronized (myEnumerator) { return doGet(key); } } - protected Value doGet(Key key) throws IOException { + protected @Nullable Value doGet(Key key) throws IOException { synchronized (PersistentEnumerator.ourLock) { myAppendCache.remove(key); final int id = tryEnumerate(key); if (id == PersistentEnumerator.NULL_ID) { return null; } - final HeaderRecord oldHeader = readValueId(id); - if (oldHeader.address == PersistentEnumerator.NULL_ID) { + final long oldHeader = readValueId(id); + if (oldHeader == PersistentEnumerator.NULL_ID) { return null; } - Pair readResult = myValueStorage.readBytes(oldHeader.address); - if (readResult.first != null && readResult.first != oldHeader.address) { + Pair readResult = myValueStorage.readBytes(oldHeader); + if (readResult.first != null && readResult.first != oldHeader) { myEnumerator.markDirty(true); - updateValueId(id, new HeaderRecord(readResult.first), oldHeader, key, 0); - if (oldHeader != HeaderRecord.EMPTY) { - myLiveAndGarbageKeysCounter++; - } + updateValueId(id, readResult.first, oldHeader, key, 0); + myLiveAndGarbageKeysCounter++; } - final DataInputStream input = new DataInputStream(new ByteArrayInputStream(readResult.second)); + final DataInputStream input = new DataInputStream(new UnsyncByteArrayInputStream(readResult.second)); try { return myValueExternalizer.read(input); } @@ -387,7 +370,7 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< if (id == PersistentEnumerator.NULL_ID) { return false; } - return readValueId(id).address != NULL_ADDR; + return readValueId(id) != NULL_ADDR; } } @@ -406,12 +389,12 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< } myEnumerator.markDirty(true); - final HeaderRecord record = readValueId(id); - if (record != HeaderRecord.EMPTY) { + final long record = readValueId(id); + if (record != NULL_ADDR) { myLiveAndGarbageKeysCounter++; } - updateValueId(id, HeaderRecord.EMPTY, record, key, 0); + updateValueId(id, NULL_ADDR, record, key, 0); } } @@ -473,10 +456,10 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< traverseAllRecords(new PersistentEnumerator.RecordsProcessor() { public boolean process(final int keyId) throws IOException { - final HeaderRecord record = readValueId(keyId); - if (record.address != NULL_ADDR) { - Pair readResult = myValueStorage.readBytes(record.address); - HeaderRecord value = new HeaderRecord(newStorage.appendBytes(new ByteSequence(readResult.second), 0)); + final long record = readValueId(keyId); + if (record != NULL_ADDR) { + Pair readResult = myValueStorage.readBytes(record); + long value = newStorage.appendBytes(new ByteSequence(readResult.second), 0); updateValueId(keyId, value, record, null, getCurrentKey()); myLiveAndGarbageKeysCounter += LIVE_KEY_MASK; } @@ -496,10 +479,10 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< } } - private HeaderRecord readValueId(final int keyId) { + private long readValueId(final int keyId) { long address = myEnumerator.myStorage.getInt(keyId + myParentValueRefOffset); if (address == 0 || address == -POSITIVE_VALUE_SHIFT) { - return HeaderRecord.EMPTY; + return NULL_ADDR; } if (address < 0) { @@ -509,7 +492,7 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< address = ((address << 32) + value) & ~USED_LONG_VALUE_MASK; } - return new HeaderRecord(address); + return address; } private int smallKeys; @@ -517,19 +500,19 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< private int transformedKeys; private int requests; - private int updateValueId(int keyId, HeaderRecord value, HeaderRecord oldValue, @Nullable Key key, int processingKey) throws IOException { - final boolean newKey = oldValue == null || oldValue.address == NULL_ADDR; + private int updateValueId(int keyId, long value, long oldValue, @Nullable Key key, int processingKey) throws IOException { + final boolean newKey = oldValue == NULL_ADDR; if (newKey) ++requests; boolean defaultSizeInfo = true; if (myCanReEnumerate) { - if (canUseIntAddressForNewRecord(value.address)) { + if (canUseIntAddressForNewRecord(value)) { defaultSizeInfo = false; - myEnumerator.myStorage.putInt(keyId + myParentValueRefOffset, -(int)(value.address + POSITIVE_VALUE_SHIFT)); + myEnumerator.myStorage.putInt(keyId + myParentValueRefOffset, -(int)(value + POSITIVE_VALUE_SHIFT)); if (newKey) ++smallKeys; } else { if (newKey && myWatermarkId == 0) myWatermarkId = keyId; - if (keyId < myWatermarkId && (oldValue == null || canUseIntAddressForNewRecord(oldValue.address))) { + if (keyId < myWatermarkId && (oldValue == NULL_ADDR || canUseIntAddressForNewRecord(oldValue))) { // keyId is result of enumerate, if we do reenumerate then it is no longer accessible unless somebody cached it myIntAddressForNewRecord = false; keyId = myEnumerator.reenumerate(key == null ? myEnumerator.getValue(keyId, processingKey) : key); @@ -539,7 +522,7 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< } if (defaultSizeInfo) { - myEnumerator.myStorage.putLong(keyId + myParentValueRefOffset, value.address | USED_LONG_VALUE_MASK); + myEnumerator.myStorage.putLong(keyId + myParentValueRefOffset, value | USED_LONG_VALUE_MASK); if (newKey) ++largeKeys; } @@ -548,21 +531,11 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< ",@"+getBaseFile().getPath()); } if (doHardConsistencyChecks) { - HeaderRecord checkRecord = readValueId(keyId); - if (checkRecord.address != value.address) { - assert false:value.address; + long checkRecord = readValueId(keyId); + if (checkRecord != value) { + assert false:value; } } return keyId; } - - private static class HeaderRecord { - final long address; - - HeaderRecord(long address) { - this.address = address; - } - - static final HeaderRecord EMPTY = new HeaderRecord(NULL_ADDR); - } } diff --git a/platform/util/src/com/intellij/util/io/PersistentHashMapValueStorage.java b/platform/util/src/com/intellij/util/io/PersistentHashMapValueStorage.java index 6edddcd02ace..26daa4379a9c 100644 --- a/platform/util/src/com/intellij/util/io/PersistentHashMapValueStorage.java +++ b/platform/util/src/com/intellij/util/io/PersistentHashMapValueStorage.java @@ -79,10 +79,13 @@ public class PersistentHashMapValueStorage { private static final int INT_LENGTH_LONG_ADDRESS = 4 + 8; public long appendBytes(ByteSequence data, long prevChunkAddress) throws IOException { + return appendBytes(data.getBytes(), data.getOffset(), data.getLength(), prevChunkAddress); + } + + public long appendBytes(byte[] data, int offset, int dataLength, long prevChunkAddress) throws IOException { assert !myCompactionMode; long result = mySize; final CacheValue appender = ourAppendersCache.get(myPath); - int dataLength = data.getLength(); int serviceFieldsSizeIncrease; try { @@ -102,7 +105,7 @@ public class PersistentHashMapValueStorage { dataOutputStream.writeLong(prevChunkAddress); serviceFieldsSizeIncrease = INT_LENGTH_LONG_ADDRESS; } - dataOutputStream.write(data.getBytes(), data.getOffset(), dataLength); + dataOutputStream.write(data, offset, dataLength); if (requests % IOStatistics.KEYS_FACTOR == 0 && IOStatistics.DEBUG) { IOStatistics.dump("Small writes:"+smallWritesCount +", bytes:"+smallWrites + ", largeWrites:"+largeWritesCount + ", bytes:"+largeWrites+", total:"+requests + "@"+myFile.getPath()); diff --git a/platform/util/src/com/intellij/util/io/UnsyncByteArrayInputStream.java b/platform/util/src/com/intellij/util/io/UnsyncByteArrayInputStream.java new file mode 100644 index 000000000000..c22a9ecb0818 --- /dev/null +++ b/platform/util/src/com/intellij/util/io/UnsyncByteArrayInputStream.java @@ -0,0 +1,69 @@ +package com.intellij.util.io; + +import java.io.InputStream; + +public class UnsyncByteArrayInputStream extends InputStream { + protected byte[] myBuffer; + private int myPosition; + private int myCount; + private int myMarkedPosition; + + public UnsyncByteArrayInputStream(byte buf[]) { + this.myBuffer = buf; + this.myPosition = 0; + this.myCount = buf.length; + } + + public int read() { + return (myPosition < myCount) ? (myBuffer[myPosition++] & 0xff) : -1; + } + + public int read(byte b[], int off, int len) { + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } + if (myPosition >= myCount) { + return -1; + } + if (myPosition + len > myCount) { + len = myCount - myPosition; + } + if (len <= 0) { + return 0; + } + System.arraycopy(myBuffer, myPosition, b, off, len); + myPosition += len; + return len; + } + + public long skip(long n) { + if (myPosition + n > myCount) { + n = myCount - myPosition; + } + if (n < 0) { + return 0; + } + myPosition += n; + return n; + } + + public int available() { + return myCount - myPosition; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public void mark(int readlimit) { + myMarkedPosition = myPosition; + } + + public void reset() { + myPosition = myMarkedPosition; + } +} diff --git a/platform/util/src/com/intellij/util/io/UnsyncByteArrayOutputStream.java b/platform/util/src/com/intellij/util/io/UnsyncByteArrayOutputStream.java new file mode 100644 index 000000000000..3ba1db36b94b --- /dev/null +++ b/platform/util/src/com/intellij/util/io/UnsyncByteArrayOutputStream.java @@ -0,0 +1,77 @@ +/* + * 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.util.io; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +public class UnsyncByteArrayOutputStream extends OutputStream { + protected byte[] myBuffer; + protected int myCount; + + public UnsyncByteArrayOutputStream() { + this(32); + } + + public UnsyncByteArrayOutputStream(int size) { + myBuffer = new byte[size]; + } + + public void write(int b) { + int newcount = myCount + 1; + if (newcount > myBuffer.length) { + myBuffer = Arrays.copyOf(myBuffer, Math.max(myBuffer.length << 1, newcount)); + } + myBuffer[myCount] = (byte)b; + myCount = newcount; + } + + public void write(byte b[], int off, int len) { + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) > b.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + int newcount = myCount + len; + if (newcount > myBuffer.length) { + myBuffer = Arrays.copyOf(myBuffer, Math.max(myBuffer.length << 1, newcount)); + } + System.arraycopy(b, off, myBuffer, myCount, len); + myCount = newcount; + } + + public void writeTo(OutputStream out) throws IOException { + out.write(myBuffer, 0, myCount); + } + + public void reset() { + myCount = 0; + } + + public byte[] toByteArray() { + return Arrays.copyOf(myBuffer, myCount); + } + + public int size() { + return myCount; + } + + public String toString() { + return new String(myBuffer, 0, myCount); + } +} diff --git a/platform/util/src/com/intellij/util/io/storage/AbstractStorage.java b/platform/util/src/com/intellij/util/io/storage/AbstractStorage.java index 8051d48d0b82..e98173172044 100644 --- a/platform/util/src/com/intellij/util/io/storage/AbstractStorage.java +++ b/platform/util/src/com/intellij/util/io/storage/AbstractStorage.java @@ -28,6 +28,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.io.PagePool; import com.intellij.util.io.RecordDataOutput; +import com.intellij.util.io.UnsyncByteArrayInputStream; import org.jetbrains.annotations.NonNls; import java.io.*; @@ -228,7 +229,7 @@ public abstract class AbstractStorage implements Disposable, Forceable { public DataInputStream readStream(int record) throws IOException { final byte[] bytes = readBytes(record); - return new DataInputStream(new ByteArrayInputStream(bytes)); + return new DataInputStream(new UnsyncByteArrayInputStream(bytes)); } protected byte[] readBytes(int record) throws IOException { diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java index dbf307d0134e..9c2c9a0d03b1 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsVFSListener.java @@ -223,7 +223,7 @@ public abstract class VcsVFSListener implements Disposable { getSingleFileDeletePromptTemplate(), myRemoveOption); } - protected void beforeContentsChange(VirtualFile file) { + protected void beforeContentsChange(VirtualFileEvent event, VirtualFile file) { } private void addFileToMove(final VirtualFile file, final String newParentPath, final String newName) { @@ -378,10 +378,8 @@ public abstract class VcsVFSListener implements Disposable { @Override public void beforeContentsChange(VirtualFileEvent event) { - if (!isEventIgnored(event, false)) { - assert !event.getFile().isDirectory(); - VcsVFSListener.this.beforeContentsChange(event.getFile()); - } + assert !event.getFile().isDirectory(); + VcsVFSListener.this.beforeContentsChange(event, event.getFile()); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java index 4463eef17b8e..bb1eb46a9e1c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java @@ -60,11 +60,13 @@ public class VcsContentAnnotationConfigurable implements Configurable { public JComponent createComponent() { JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); myHighlightRecentlyChanged = new JCheckBox("Show changed in last"); - myHighlightRecentlyChanged.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + myHighlightRecentlyChanged.setBorder(BorderFactory.createEmptyBorder(0, 1, 0, 1)); myHighlightInterval = new JSpinner(new SpinnerNumberModel(1, 1, VcsContentAnnotationSettings.ourMaxDays, 1)); wrapper.add(myHighlightRecentlyChanged); wrapper.add(myHighlightInterval); - wrapper.add(new JLabel("days")); + final JLabel days = new JLabel("days"); + days.setBorder(BorderFactory.createEmptyBorder(0, 1, 0, 1)); + wrapper.add(days); myHighlightRecentlyChanged.addActionListener(new ActionListener() { @Override 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 177f04560a45..6a0c734fb28b 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 @@ -151,7 +151,6 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { debugProcess.registerAdditionalContent(myUi); RunContentBuilder.addAdditionalConsoleEditorActions(myConsole, consoleContent); - myUi.addContent(consoleContent, 0, PlaceInGrid.bottom, false); if (ApplicationManager.getApplication().isUnitTestMode()) { return myRunContentDescriptor; diff --git a/plugins/InspectionGadgets/src/META-INF/plugin.xml b/plugins/InspectionGadgets/src/META-INF/plugin.xml index 6569e065320d..cc4fbc49ed02 100644 --- a/plugins/InspectionGadgets/src/META-INF/plugin.xml +++ b/plugins/InspectionGadgets/src/META-INF/plugin.xml @@ -1824,10 +1824,6 @@ bundle="com.siyeh.InspectionGadgetsBundle" key="string.buffer.must.have.initial.capacity.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.performance.issues" enabledByDefault="false" level="WARNING" implementationClass="com.siyeh.ig.performance.StringBufferMustHaveInitialCapacityInspection"/> - + #ref replaceable wit set.replaceable.by.enum.set.problem.descriptor=#ref replaceable with EnumSet #loc inner.class.may.be.static.problem.descriptor=Inner class #ref may be 'static' #loc string.buffer.must.have.initial.capacity.problem.descriptor=#ref without initial capacity #loc -string.buffer.replaceable.by.string.builder.problem.descriptor=StringBuffer #ref may be declared as StringBuilder #loc -string.buffer.replaceable.by.string.problem.descriptor=Constant StringBuffer #ref may be declared as String #loc -string.replaceable.by.string.buffer.problem.descriptor=Non-constant String #ref should probably be declared as StringBuilder #loc +string.buffer.replaceable.by.string.builder.problem.descriptor=StringBuffer #ref may be declared as ''StringBuilder'' #loc +string.buffer.replaceable.by.string.problem.descriptor={0} #ref can be replaced with ''String'' #loc +string.replaceable.by.string.buffer.problem.descriptor=Non-constant String #ref should probably be declared as ''StringBuilder'' #loc collections.must.have.initial.capacity.problem.descriptor=#ref without initial capacity #loc string.concatenation.in.loops.problem.descriptor=String concatenation #ref in loop #loc string.concatenation.inside.string.buffer.append.problem.descriptor=String concatenation as argument to {0}.#ref() call #loc @@ -1303,7 +1303,7 @@ manual.array.to.collection.copy.replace.quickfix=Replace with 'Collections.addAl method.may.be.static.only.option=Only check private or final methods method.may.be.static.empty.option=Ignore empty methods random.double.for.random.integer.replace.quickfix=Replace with 'nextInt()' -string.buffer.replaceable.by.string.builder.replace.quickfix=Replace with StringBuilder +string.buffer.replaceable.by.string.builder.replace.quickfix=Replace with 'StringBuilder' string.buffer.to.string.in.concatenation.remove.quickfix=Remove 'toString()' string.concatenation.in.loops.only.option=Only warn if string is repeatedly appended string.concatenation.inside.string.buffer.append.replace.quickfix=Replace with chained 'append()' calls @@ -1967,4 +1967,6 @@ ambiguous.field.access.display.name=Inherited field accessed while local variabl ambiguous.field.access.hides.local.variable.problem.descriptor=Field #ref from super class ''{0}'' accessed, while local variable access might be expected #loc ambiguous.field.access.hides.parameter.problem.descriptor=Field #ref from super class ''{0}'' accessed, while parameter access might be expected #loc ambiguous.field.access.hides.field.problem.descriptor=Field #ref from super class ''{0}'' accessed, while field access from surrounding class might be expected #loc -ambiguous.field.access.quickfix=Qualify field access with 'super.' \ No newline at end of file +ambiguous.field.access.quickfix=Qualify field access with 'super.' +string.builder.replaceable.by.string.quickfix=Replace 'StringBuilder' with 'String' +string.buffer.replaceable.by.string.quickfix=Replace 'StringBuffer' with 'String' \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/performance/StringBufferReplaceableByStringInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/performance/StringBufferReplaceableByStringInspection.java deleted file mode 100644 index 5d6be63483a9..000000000000 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/performance/StringBufferReplaceableByStringInspection.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2003-2010 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.PsiTreeUtil; -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.BaseInspection; -import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.TypeUtils; -import com.siyeh.ig.psiutils.VariableAccessUtils; -import org.jetbrains.annotations.NotNull; - -public class StringBufferReplaceableByStringInspection - extends BaseInspection { - - @Override - @NotNull - public String getDisplayName() { - return InspectionGadgetsBundle.message( - "string.buffer.replaceable.by.string.display.name"); - } - - @Override - @NotNull - public String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "string.buffer.replaceable.by.string.problem.descriptor"); - } - - @Override - public BaseInspectionVisitor buildVisitor() { - return new StringBufferReplaceableByStringBuilderVisitor(); - } - - private static class StringBufferReplaceableByStringBuilderVisitor - extends BaseInspectionVisitor { - - @Override - public void visitLocalVariable(@NotNull PsiLocalVariable variable) { - super.visitLocalVariable(variable); - - final PsiCodeBlock codeBlock = - PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); - if (codeBlock == null) { - return; - } - final PsiType type = variable.getType(); - if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUFFER, - type) && - !TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUILDER, type)) { - return; - } - final PsiExpression initializer = variable.getInitializer(); - if (initializer == null) { - return; - } - if (!isNewStringBufferOrStringBuilder(initializer)) { - return; - } - if (VariableAccessUtils.variableIsAssigned(variable, codeBlock)) { - return; - } - if (VariableAccessUtils.variableIsAssignedFrom(variable, codeBlock)) { - return; - } - if (VariableAccessUtils.variableIsReturned(variable, codeBlock)) { - return; - } - if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, - codeBlock)) { - return; - } - if (variableIsModified(variable, codeBlock)) { - return; - } - registerVariableError(variable); - } - - public static boolean variableIsModified(PsiVariable variable, - PsiElement context) { - final VariableIsModifiedVisitor visitor = - new VariableIsModifiedVisitor(variable); - context.accept(visitor); - return visitor.isModified(); - } - - private static boolean isNewStringBufferOrStringBuilder( - PsiExpression expression) { - if (expression == null) { - return false; - } - else if (expression instanceof PsiNewExpression) { - return true; - } - else if (expression instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression methodCallExpression = - (PsiMethodCallExpression)expression; - if (!VariableIsModifiedVisitor.isStringBufferUpdate( - methodCallExpression)) { - return false; - } - final PsiReferenceExpression methodExpression = - methodCallExpression.getMethodExpression(); - final PsiExpression qualifier = - methodExpression.getQualifierExpression(); - return isNewStringBufferOrStringBuilder(qualifier); - } - return false; - } - } -} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/StringBufferReplaceableByStringInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/StringBufferReplaceableByStringInspection.java new file mode 100644 index 000000000000..9d56ab1b5c69 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/StringBufferReplaceableByStringInspection.java @@ -0,0 +1,213 @@ +/* + * Copyright 2003-2012 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.style; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.TypeUtils; +import com.siyeh.ig.psiutils.VariableAccessUtils; +import org.jetbrains.annotations.NotNull; + +public class StringBufferReplaceableByStringInspection extends BaseInspection { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + final String typeText = ((PsiType)infos[0]).getPresentableText(); + return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.problem.descriptor", typeText); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final String typeText = ((PsiType)infos[0]).getCanonicalText(); + return new StringBufferReplaceableByStringFix(CommonClassNames.JAVA_LANG_STRING_BUILDER.equals(typeText)); + } + + private static class StringBufferReplaceableByStringFix extends InspectionGadgetsFix { + + private final boolean isStringBuilder; + + private StringBufferReplaceableByStringFix(boolean isStringBuilder) { + this.isStringBuilder = isStringBuilder; + } + + @NotNull + @Override + public String getName() { + if (isStringBuilder) { + return InspectionGadgetsBundle.message("string.builder.replaceable.by.string.quickfix"); + } else { + return InspectionGadgetsBundle.message("string.buffer.replaceable.by.string.quickfix"); + } + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + final PsiElement parent = element.getParent(); + if (!(parent instanceof PsiVariable)) { + return; + } + final PsiVariable variable = (PsiVariable)parent; + final PsiTypeElement originalTypeElement = variable.getTypeElement(); + if (originalTypeElement == null) { + return; + } + final PsiExpression initializer = variable.getInitializer(); + if (initializer == null) { + return; + } + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiClassType javaLangString = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_STRING, variable.getResolveScope()); + final PsiTypeElement typeElement = factory.createTypeElement(javaLangString); + final StringBuilder newInitializer = buildReplacementInitializer(initializer, new StringBuilder()); + if (newInitializer == null) { + return; + } + originalTypeElement.replace(typeElement); + replaceExpression(initializer, newInitializer.toString()); + } + + private static StringBuilder buildReplacementInitializer(PsiExpression initializer, StringBuilder result) { + if (initializer instanceof PsiNewExpression) { + final PsiNewExpression newExpression = (PsiNewExpression)initializer; + final PsiExpressionList argumentList = newExpression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length == 0) { + result.append("\"\""); + } else if (arguments.length == 1) { + final PsiExpression argument = arguments[0]; + final PsiType type = argument.getType(); + if (PsiType.INT.equals(type)) { + result.append("\"\""); + } else { + result.append(argument.getText()); + } + } else { + return null; + } + } else if (initializer instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)initializer; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + result = buildReplacementInitializer(qualifier, result); + if (result == null) { + return null; + } + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length != 1) { + return null; + } + final PsiExpression argument = arguments[0]; + result.append('+'); + result.append(argument.getText()); + } else { + return null; + } + return result; + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new StringBufferReplaceableByStringVisitor(); + } + + private static class StringBufferReplaceableByStringVisitor extends BaseInspectionVisitor { + + @Override + public void visitLocalVariable(@NotNull PsiLocalVariable variable) { + super.visitLocalVariable(variable); + final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); + if (codeBlock == null) { + return; + } + final PsiType type = variable.getType(); + if (!TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUFFER, type) && + !TypeUtils.typeEquals(CommonClassNames.JAVA_LANG_STRING_BUILDER, type)) { + return; + } + final PsiExpression initializer = variable.getInitializer(); + if (initializer == null) { + return; + } + if (!isNewStringBufferOrStringBuilder(initializer)) { + return; + } + if (VariableAccessUtils.variableIsAssigned(variable, codeBlock)) { + return; + } + if (VariableAccessUtils.variableIsAssignedFrom(variable, codeBlock)) { + return; + } + if (VariableAccessUtils.variableIsReturned(variable, codeBlock)) { + return; + } + if (VariableAccessUtils.variableIsPassedAsMethodArgument(variable, codeBlock)) { + return; + } + if (variableIsModified(variable, codeBlock)) { + return; + } + registerVariableError(variable, type); + } + + public static boolean variableIsModified(PsiVariable variable, PsiElement context) { + final VariableIsModifiedVisitor visitor = new VariableIsModifiedVisitor(variable); + context.accept(visitor); + return visitor.isModified(); + } + + private static boolean isNewStringBufferOrStringBuilder(PsiExpression expression) { + if (expression == null) { + return false; + } + else if (expression instanceof PsiNewExpression) { + return true; + } + else if (expression instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + if (!isAppend(methodCallExpression)) { + return false; + } + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); + return isNewStringBufferOrStringBuilder(qualifier); + } + return false; + } + + public static boolean isAppend(PsiMethodCallExpression methodCallExpression) { + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + return "append".equals(methodName); + } + } +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/performance/VariableIsModifiedVisitor.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/VariableIsModifiedVisitor.java similarity index 73% rename from plugins/InspectionGadgets/src/com/siyeh/ig/performance/VariableIsModifiedVisitor.java rename to plugins/InspectionGadgets/src/com/siyeh/ig/style/VariableIsModifiedVisitor.java index 0a5651be08ea..748ce1bc5966 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/performance/VariableIsModifiedVisitor.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/VariableIsModifiedVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2005 Dave Griffith + * Copyright 2003-2012 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.siyeh.ig.performance; +package com.siyeh.ig.style; import com.intellij.psi.*; import org.jetbrains.annotations.NonNls; @@ -25,14 +25,15 @@ import java.util.Set; class VariableIsModifiedVisitor extends JavaRecursiveElementVisitor { - @NonNls private static final Set updateNames = - new HashSet(9); + @NonNls private static final Set updateNames = new HashSet(9); static { updateNames.add("append"); updateNames.add("appendCodePoint"); + updateNames.add("capacity"); // does not strictly modify, but is not a String method updateNames.add("delete"); updateNames.add("deleteCharAt"); + updateNames.add("ensureCapacity"); // does not strictly modify, but is not a String method updateNames.add("insert"); updateNames.add("replace"); updateNames.add("reverse"); @@ -44,7 +45,6 @@ class VariableIsModifiedVisitor extends JavaRecursiveElementVisitor { private final PsiVariable variable; VariableIsModifiedVisitor(PsiVariable variable) { - super(); this.variable = variable; } @@ -65,28 +65,23 @@ class VariableIsModifiedVisitor extends JavaRecursiveElementVisitor { if (!isStringBufferUpdate(call)) { return; } - final PsiReferenceExpression methodExpression = - call.getMethodExpression(); - final PsiExpression qualifier = - methodExpression.getQualifierExpression(); + final PsiReferenceExpression methodExpression = call.getMethodExpression(); + final PsiExpression qualifier = methodExpression.getQualifierExpression(); if (!(qualifier instanceof PsiReferenceExpression)) { return; } - final PsiReferenceExpression reference = - (PsiReferenceExpression)qualifier; + final PsiReferenceExpression reference = (PsiReferenceExpression)qualifier; final PsiElement referent = reference.resolve(); if (variable.equals(referent)) { modified = true; } } - public static boolean isStringBufferUpdate( - @Nullable PsiMethodCallExpression methodCallExpression) { + public static boolean isStringBufferUpdate(@Nullable PsiMethodCallExpression methodCallExpression) { if (methodCallExpression == null) { return false; } - final PsiReferenceExpression methodExpression = - methodCallExpression.getMethodExpression(); + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); final String methodName = methodExpression.getReferenceName(); return updateNames.contains(methodName); } diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/StringBufferReplaceableByString.html b/plugins/InspectionGadgets/src/inspectionDescriptions/StringBufferReplaceableByString.html index 2273ce2f01f6..832b4e495ec3 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/StringBufferReplaceableByString.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/StringBufferReplaceableByString.html @@ -1,8 +1,8 @@ -This inspection reports any variables declared as java.lang.StringBuffer -and java.lang.StringBuilder which are -effectively constant, and may thus be declared more efficiently declared as java.lang.String. +This inspection reports any variables declared as or uses of java.lang.StringBuffer and java.lang.StringBuilder +which are effectively constant. These may be replaced with java.lang.String expressions which results in simpler and possibly +more efficient code.

Powered by InspectionGadgets diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/StringBufferReplaceableByStringInspection.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/StringBufferReplaceableByStringInspection.java deleted file mode 100644 index 085519f5c4ab..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/StringBufferReplaceableByStringInspection.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.siyeh.igtest.performance; - -public class StringBufferReplaceableByStringInspection { - public void foo() - { - final StringBuffer buffer = new StringBuffer(); - System.out.println(buffer.toString()); - } - - public void foo2() - { - final StringBuffer buffer = new StringBuffer("foo").append("bar"); - System.out.println(buffer.toString()); - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_buffer_replaceable_by_string/StringBufferReplaceableByString.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_buffer_replaceable_by_string/StringBufferReplaceableByString.java new file mode 100644 index 000000000000..6c13850a1212 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_buffer_replaceable_by_string/StringBufferReplaceableByString.java @@ -0,0 +1,27 @@ +package com.siyeh.igtest.performance.string_buffer_replaceable_by_string; + +public class StringBufferReplaceableByString { + + StringBuilder foo() { + StringBuilder builder = new StringBuilder("asdfasdfasdf"); + StringBuffer buffer = new StringBuffer("test"); + StringBuilder result = new StringBuilder("return"); + return result; + } + + public void foo1() + { + final StringBuffer buffer = new StringBuffer(); + System.out.println(buffer.toString()); + } + + public void foo2() + { + final StringBuffer buffer = new StringBuffer("foo").append("bar"); + System.out.println(buffer.toString()); + } + + public void bar(int i) { + System.out.println(new StringBuilder("asdf").append(i).toString()); + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_buffer_replaceable_by_string/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_buffer_replaceable_by_string/expected.xml new file mode 100644 index 000000000000..fbc4c4e90c9d --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/string_buffer_replaceable_by_string/expected.xml @@ -0,0 +1,30 @@ + + + + StringBufferReplaceableByString.java + 6 + 'StringBuffer' can be replaced with 'String' + <code>StringBuilder builder</code> can be replaced with 'String' #loc + + + + StringBufferReplaceableByString.java + 7 + 'StringBuffer' can be replaced with 'String' + <code>StringBuffer buffer</code> can be replaced with 'String' #loc + + + + StringBufferReplaceableByString.java + 14 + 'StringBuffer' can be replaced with 'String' + <code>StringBuffer buffer</code> can be replaced with 'String' #loc + + + + StringBufferReplaceableByString.java + 20 + 'StringBuffer' can be replaced with 'String' + <code>StringBuffer buffer</code> can be replaced with 'String' #loc + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/StringBufferReplaceableByStringInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/StringBufferReplaceableByStringInspectionTest.java new file mode 100644 index 000000000000..b27d8cd46527 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/StringBufferReplaceableByStringInspectionTest.java @@ -0,0 +1,11 @@ +package com.siyeh.ig.style; + +import com.siyeh.ig.IGInspectionTestCase; + +public class StringBufferReplaceableByStringInspectionTest extends IGInspectionTestCase { + + public void test() throws Exception { + doTest("com/siyeh/igtest/performance/string_buffer_replaceable_by_string", + new StringBufferReplaceableByStringInspection()); + } +} \ No newline at end of file diff --git a/plugins/android/resources/messages/AndroidBundle.properties b/plugins/android/resources/messages/AndroidBundle.properties index feb398e022a1..4ed608cd7172 100644 --- a/plugins/android/resources/messages/AndroidBundle.properties +++ b/plugins/android/resources/messages/AndroidBundle.properties @@ -1,6 +1,6 @@ command.0.execution.failed.with.exit.code.1=Command \"{0}\" execution failed with exit code {1} aidl.filetype.description=Android IDL files -add.string.resource.intention.text=Add a string resource +add.string.resource.intention.text=Extract string resource resource.name=Resource name intention.family=Android package.not.found.error=Package is not specified in the manifest file @@ -8,8 +8,8 @@ cannot.resolve.flag.error=Cannot resolve flag cannot.resolve.format.error=Cannot resolve format create.resource.quickfix.name=Create resource {0} in {1} quick.fixes.family=Android Quick Fixes -not.resource.file.error="File {0} is not resource file" -check.resource.dir.error=Check if resource directory exists +not.resource.file.error=File {0} is not resource file +check.resource.dir.error=Cannot find resource directory for module {0} choose.device.dialog.title=Choose Device replace.avd.question=AVD {0} already exists. Replace? create.avd.dialog.title=Create Android Virtual Device @@ -147,6 +147,7 @@ create.run.configuration.title=Create Android Run Configuration file.must.be.under.module.error=Cannot compute relative path for {0} android.extract.package.choose.dest.apk=Choose destination APK file android.cannot.create.file.error=Cannot create file {0} +android.cannot.create.dir.error=Cannot create directory {0} android.manifest.not.found.error=AndroidManifest.xml doesn't exist or has incorrect root tag android.apt.settings.title=AAPT Compiler android.aidl.settings.title=AIDL Compiler @@ -305,4 +306,20 @@ android.facet.settings.custom.debug.keystore.label=C&ustom debug keystore\: android.export.package.run.proguard.label=Run &ProGuard android.export.package.proguad.config.label=C&onfig file path\: android.extract.package.specify.proguard.cfg.path.error=Please specify ProGuard config file path -android.inspections.lint.name=Android Lint Validation \ No newline at end of file +android.inspections.lint.name=Android Lint Validation +android.lint.quickfixes.family=Android Lint Quick Fixes +android.lint.inspections.add.content.description=Add 'contentDescription' attribute +android.lint.inspections.scroll.view.children=ScrollView element can have only one child +android.lint.inspections.deprecated=Deprecated XML elements +android.lint.inspections.missing.prefix=Missing android XML namespace +android.lint.inspections.adapter.view.children=AdapterView element cannot have children +android.lint.inspections.add.android.prefix=Add android prefix +android.lint.inspections.replace.with.zero.dp=Replace size attribute with 0dp +android.lint.inspections.set.baseline.attribute=Set 'baselineAligned' attribute +android.lint.inspections.remove.attribute=Remove attribute +android.lint.inspections.convert.to.dp=Convert to \\"dp\\"... +android.lint.inspections.set.to.wrap.content=Replace size attribute with 'wrap_content' +android.lint.inspections.add.permission.attribute=Add 'permission' attribute +android.lint.inspections.add.input.type.attribute=Add 'inputType' attribute +android.lint.inspections.remove.unnecessary.view=Remove unnecessary view +android.lint.inspections.replace.with.suggested.characters=Replace with suggested characters \ No newline at end of file diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateResourceDialog.form b/plugins/android/src/org/jetbrains/android/actions/CreateResourceDialog.form index efaa03fc30f8..560b404bdd2d 100644 --- a/plugins/android/src/org/jetbrains/android/actions/CreateResourceDialog.form +++ b/plugins/android/src/org/jetbrains/android/actions/CreateResourceDialog.form @@ -3,7 +3,7 @@ - + diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.form b/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.form new file mode 100644 index 000000000000..e259ae3a5dad --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.form @@ -0,0 +1,93 @@ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java b/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java new file mode 100644 index 000000000000..dae344b92ced --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/actions/CreateXmlResourceDialog.java @@ -0,0 +1,264 @@ +package org.jetbrains.android.actions; + +import com.android.AndroidConstants; +import com.android.ide.common.resources.configuration.FolderConfiguration; +import com.android.resources.ResourceFolderType; +import com.android.resources.ResourceType; +import com.intellij.CommonBundle; +import com.intellij.ide.ui.ListCellRendererWrapper; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.containers.HashSet; +import org.jetbrains.android.dom.resources.ResourceElement; +import org.jetbrains.android.dom.resources.Resources; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.facet.AndroidRootUtil; +import org.jetbrains.android.resourceManagers.ResourceManager; +import org.jetbrains.android.uipreview.DeviceConfiguratorPanel; +import org.jetbrains.android.uipreview.InvalidOptionValueException; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidResourceUtil; +import org.jetbrains.android.util.AndroidUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Set; + +/** + * @author Eugene.Kudelevsky + */ +public class CreateXmlResourceDialog extends DialogWrapper { + private JPanel myPanel; + private JPanel myDeviceConfigurationWrapper; + private JTextField myNameField; + private JComboBox myModuleCombo; + private JBLabel myModuleLabel; + private JTextField myDirectoryNameField; + private JBLabel myErrorLabel; + private JTextField myFileNameField; + + private final DeviceConfiguratorPanel myDeviceConfiguratorPanel; + private final Module myModule; + private final ResourceType myResourceType; + + public CreateXmlResourceDialog(@NotNull Module module, @NotNull ResourceType resourceType) { + super(module.getProject()); + + myResourceType = resourceType; + + final Set modulesSet = new HashSet(); + modulesSet.add(module); + + for (AndroidFacet depFacet : AndroidUtils.getAllAndroidDependencies(module, true)) { + modulesSet.add(depFacet.getModule()); + } + + assert modulesSet.size() > 0; + + if (modulesSet.size() == 1) { + myModule = module; + myModuleLabel.setVisible(false); + myModuleCombo.setVisible(false); + } + else { + myModule = null; + + final Module[] modules = modulesSet.toArray(new Module[modulesSet.size()]); + Arrays.sort(modules, new Comparator() { + @Override + public int compare(Module m1, Module m2) { + return m1.getName().compareTo(m2.getName()); + } + }); + + myModuleCombo.setModel(new DefaultComboBoxModel(modules)); + myModuleCombo.setSelectedItem(module); + + myModuleCombo.setRenderer(new ListCellRendererWrapper(myModuleCombo.getRenderer()) { + @Override + public void customize(JList list, Module module, int index, boolean selected, boolean hasFocus) { + setText(module.getName()); + setIcon(ModuleType.get(module).getNodeIcon(false)); + } + }); + } + + myDeviceConfiguratorPanel = new DeviceConfiguratorPanel(null) { + @Override + public void applyEditors() { + try { + doApplyEditors(); + + final FolderConfiguration config = myDeviceConfiguratorPanel.getConfiguration(); + myErrorLabel.setText(""); + myDirectoryNameField.setText(config.getFolderName(ResourceFolderType.VALUES)); + } + catch (InvalidOptionValueException e) { + myErrorLabel.setText("" + e.getMessage() + ""); + myDirectoryNameField.setText(AndroidConstants.FD_RES_VALUES); + } + } + }; + myDeviceConfigurationWrapper.add(myDeviceConfiguratorPanel, BorderLayout.CENTER); + + final String defaultResFileName = ResourceManager.getDefaultResourceFileName(resourceType.getName()); + if (defaultResFileName != null) { + myFileNameField.setText(defaultResFileName); + } + myDirectoryNameField.setText(AndroidConstants.FD_RES_VALUES); + myDeviceConfiguratorPanel.updateAll(); + + init(); + } + + @Override + protected ValidationInfo doValidate() { + final String resourceName = getResourceName(); + final Module selectedModule = getModule(); + final String directoryName = getDirectoryName(); + final String fileName = getFileName(); + + if (resourceName.length() == 0) { + return new ValidationInfo("specify resource name", myNameField); + } + else if (!AndroidResourceUtil.isCorrectAndroidResourceName(resourceName)) { + return new ValidationInfo(resourceName + " is not correct resource name", myNameField); + } + else if (fileName.length() == 0) { + return new ValidationInfo("specify file name", myFileNameField); + } + else if (selectedModule == null) { + return new ValidationInfo("specify module", myModuleCombo); + } + else if (!ResourceFolderType.VALUES.getName().equals( + ResourceManager.getResourceTypeByDirName(directoryName))) { + return new ValidationInfo("directory name is not appropriate for value resources"); + } + + final ValidationInfo info = checkIfResourceAlreadyExists(selectedModule, resourceName, myResourceType, directoryName, fileName); + if (info != null) { + return info; + } + + try { + myDeviceConfiguratorPanel.doApplyEditors(); + } + catch (InvalidOptionValueException e) { + return new ValidationInfo("fix errors in configuration editor"); + } + + return null; + } + + @Nullable + private static ValidationInfo checkIfResourceAlreadyExists(@NotNull Module selectedModule, + @NotNull String resourceName, + @NotNull ResourceType resourceType, + @NotNull String directoryName, + @NotNull String fileName) { + if (resourceName.length() == 0 || + directoryName.length() == 0 || + fileName.length() == 0) { + return null; + } + + final VirtualFile resourceDir = AndroidRootUtil.getResourceDir(selectedModule); + if (resourceDir == null) { + return null; + } + + final VirtualFile resourceSubdir = resourceDir.findChild(directoryName); + if (resourceSubdir == null) { + return null; + } + + final VirtualFile resFile = resourceSubdir.findChild(fileName); + if (resFile == null) { + return null; + } + + if (resFile.getFileType() != StdFileTypes.XML) { + return new ValidationInfo("File " + FileUtil.toSystemDependentName(resFile.getPath()) + " is not XML file"); + } + + final Resources resources = AndroidUtils.loadDomElement(selectedModule, resFile, Resources.class); + if (resources == null) { + return new ValidationInfo(AndroidBundle.message("not.resource.file.error", FileUtil.toSystemDependentName(resFile.getPath()))); + } + + for (ResourceElement element : ResourceManager.getValueResources(resourceType.getName(), resources)) { + if (resourceName.equals(element.getName().getValue())) { + return new ValidationInfo("resource '" + resourceName + "' already exists in " + FileUtil.toSystemDependentName( + resFile.getPath())); + } + } + return null; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myNameField; + } + + @Override + protected void doOKAction() { + final String resourceName = getResourceName(); + final String fileName = getFileName(); + final String dirName = getDirectoryName(); + final Module module = getModule(); + + if (resourceName.length() == 0) { + Messages.showErrorDialog(myPanel, "Resource name is not specified", CommonBundle.getErrorTitle()); + } + else if (!AndroidResourceUtil.isCorrectAndroidResourceName(resourceName)) { + Messages.showErrorDialog(myPanel, resourceName + " is not correct resource name", CommonBundle.getErrorTitle()); + } + else if (fileName.length() == 0) { + Messages.showErrorDialog(myPanel, "File name is not specified", CommonBundle.getErrorTitle()); + } + else if (dirName.length() == 0) { + Messages.showErrorDialog(myPanel, "Directory name is not specified", CommonBundle.getErrorTitle()); + } + else if (module == null) { + Messages.showErrorDialog(myPanel, "Module is not specified", CommonBundle.getErrorTitle()); + } + else { + super.doOKAction(); + } + } + + @NotNull + public String getResourceName() { + return myNameField.getText().trim(); + } + + @NotNull + public String getDirectoryName() { + return myDirectoryNameField.getText().trim(); + } + + @NotNull + public String getFileName() { + return myFileNameField.getText().trim(); + } + + @Nullable + public Module getModule() { + return myModule != null ? myModule : (Module)myModuleCombo.getSelectedItem(); + } + + @Override + protected JComponent createCenterPanel() { + return myPanel; + } +} diff --git a/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java b/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java index ad7fee9ff8a5..e77187a20cde 100644 --- a/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java +++ b/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java @@ -165,7 +165,12 @@ public class ResourceReferenceConverter extends ResolvingConverter getResourceTypes(ConvertContext context) { - ResourceType resourceType = context.getInvocationElement().getAnnotation(ResourceType.class); + return getResourceTypes(context.getInvocationElement()); + } + + @NotNull + public Set getResourceTypes(@NotNull DomElement element) { + ResourceType resourceType = element.getAnnotation(ResourceType.class); Set types = new HashSet(myResourceTypes); if (resourceType != null) { String s = resourceType.value(); diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/AddMissingPrefixQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/AddMissingPrefixQuickFix.java new file mode 100644 index 000000000000..67b4c390ad08 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/AddMissingPrefixQuickFix.java @@ -0,0 +1,72 @@ +package org.jetbrains.android.inspections.lint; + +import com.android.sdklib.SdkConstants; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.xml.XmlExtension; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; + +/** + * @author Eugene.Kudelevsky + */ +class AddMissingPrefixQuickFix implements AndroidLintQuickFix { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.AddMissingPrefixQuickFix"); + + @Override + public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) { + final XmlAttribute attribute = PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class, false); + if (attribute == null) { + return; + } + + final XmlTag tag = attribute.getParent(); + if (tag == null) { + LOG.debug("tag is null"); + return; + } + + String androidNsPrefix = tag.getPrefixByNamespace(SdkConstants.NS_RESOURCES); + + if (androidNsPrefix == null) { + final PsiFile file = tag.getContainingFile(); + final XmlExtension extension = XmlExtension.getExtension(file); + + if (extension == null) { + LOG.debug("Cannot get XmlExtension for file + " + file); + return; + } + + if (!(file instanceof XmlFile)) { + LOG.debug(file + " is not XmlFile"); + return; + } + + final XmlFile xmlFile = (XmlFile)file; + final String defaultPrefix = "android"; + extension.insertNamespaceDeclaration(xmlFile, null, Collections.singleton(SdkConstants.NS_RESOURCES), defaultPrefix, null); + androidNsPrefix = defaultPrefix; + } + attribute.setName(androidNsPrefix + ':' + attribute.getLocalName()); + } + + @Override + public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) { + return PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class, false) != null; + } + + @NotNull + @Override + public String getName() { + return AndroidBundle.message("android.lint.inspections.add.android.prefix"); + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidAddStringResourceQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidAddStringResourceQuickFix.java new file mode 100644 index 000000000000..0a614f88a0d4 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidAddStringResourceQuickFix.java @@ -0,0 +1,47 @@ +package org.jetbrains.android.inspections.lint; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.android.intentions.AndroidAddStringResourceAction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidAddStringResourceQuickFix extends AndroidAddStringResourceAction { + private final PsiElement myStartElement; + + public AndroidAddStringResourceQuickFix(@NotNull PsiElement startElement) { + myStartElement = startElement; + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return getAttributeValue(myStartElement) != null; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + invokeIntention(project, editor, file, null); + } + + public void invokeIntention(Project project, Editor editor, PsiFile file, String resName) { + final XmlAttributeValue attributeValue = getAttributeValue(myStartElement); + if (attributeValue != null) { + doInvoke(project, editor, file, resName, attributeValue); + } + } + + @Nullable + private static XmlAttributeValue getAttributeValue(@NotNull PsiElement element) { + final XmlAttribute attribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class); + return attribute != null ? attribute.getValueElement() : null; + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintExternalAnnotator.java b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintExternalAnnotator.java index 8e49234ba547..b2a909b18df7 100644 --- a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintExternalAnnotator.java +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintExternalAnnotator.java @@ -7,11 +7,11 @@ import com.android.tools.lint.client.api.Lint; import com.android.tools.lint.detector.api.Issue; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.InspectionProfile; -import com.intellij.codeInspection.ex.CustomEditInspectionToolsSettingsAction; import com.intellij.codeInspection.ex.DisableInspectionToolAction; -import com.intellij.codeInspection.ex.InspectionToolWrapper; +import com.intellij.codeInspection.ex.EditInspectionToolsSettingsAction; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.ExternalAnnotator; @@ -22,7 +22,10 @@ import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Iconable; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.PsiElement; @@ -30,6 +33,7 @@ import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidRootUtil; +import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -62,7 +66,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator pair = getHighlighLevelAndInspection(issue, file); + final Pair pair = AndroidLintUtil.getHighlighLevelAndInspection(issue, file); if (pair == null) { continue; } final AndroidLintInspectionBase inspection = pair.getFirst(); final HighlightDisplayLevel displayLevel = pair.getSecond(); - + final Annotation annotation = createAnnotation(holder, message, range, displayLevel); if (inspection != null) { final HighlightDisplayKey key = HighlightDisplayKey.find(inspection.getShortName()); - + if (key != null) { - annotation.registerFix(new MyDisableInspectionFix(key)); - annotation.registerFix(new CustomEditInspectionToolsSettingsAction(key, new Computable() { - @Override - public String compute() { - return "Edit '" + inspection.getDisplayName() + "' inspection settings"; + final PsiElement startElement = file.findElementAt(range.getStartOffset()); + final PsiElement endElement = file.findElementAt(range.getEndOffset() - 1); + + if (startElement != null && endElement != null) { + for (AndroidLintQuickFix fix : inspection.getQuickFixes(message)) { + if (fix.isApplicable(startElement, endElement, false)) { + annotation.registerFix(new MyFixingIntention(fix, startElement, endElement)); + } } - })); + + for (IntentionAction intention : inspection.getIntentions(startElement, endElement)) { + annotation.registerFix(intention); + } + } + + annotation.registerFix(new MyDisableInspectionFix(key)); + annotation.registerFix(new MyEditInspectionToolsSettingsAction(key, inspection)); } } } @@ -189,36 +203,6 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator getHighlighLevelAndInspection(@NotNull Issue issue, - @NotNull PsiElement context) { - final String inspectionShortName = AndroidLintInspectionBase.getInspectionShortNameByIssue(issue); - if (inspectionShortName == null) { - return null; - } - - final HighlightDisplayKey key = HighlightDisplayKey.find(inspectionShortName); - if (key == null) { - return null; - } - - final InspectionProfile profile = InspectionProjectProfileManager.getInstance(context.getProject()).getInspectionProfile(); - if (!profile.isToolEnabled(key, context)) { - return null; - } - - final InspectionToolWrapper toolWrapper = - (InspectionToolWrapper)profile.getInspectionTool(inspectionShortName, context); - if (toolWrapper == null) { - return null; - } - - final AndroidLintInspectionBase inspection = (AndroidLintInspectionBase)toolWrapper.getTool(); - final HighlightDisplayLevel errorLevel = profile.getErrorLevel(key, context); - return new Pair(inspection, - errorLevel != null ? errorLevel : HighlightDisplayLevel.WARNING); - } - private static class MyDisableInspectionFix implements IntentionAction, Iconable { private final DisableInspectionToolAction myDisableInspectionToolAction; @@ -258,4 +242,90 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator getIssues() { @@ -128,13 +125,21 @@ class AndroidLintGlobalInspectionContext implements GlobalInspectionContextExten private final Project myProject; private final Map>> myProblemMap; private final AnalysisScope myScope; + private final Collection myIssues; private MyLintClient(@NotNull Project project, @NotNull Map>> problemMap, - @NotNull AnalysisScope scope) { + @NotNull AnalysisScope scope, + @NotNull Collection issues) { myProject = project; myProblemMap = problemMap; myScope = scope; + myIssues = issues; + } + + @Override + public Configuration getConfiguration(com.android.tools.lint.detector.api.Project project) { + return new IntellijLintConfiguration(myIssues); } @Override @@ -248,6 +253,9 @@ class AndroidLintGlobalInspectionContext implements GlobalInspectionContextExten @Override public List getJavaSourceFolders(com.android.tools.lint.detector.api.Project project) { final Module module = findModuleForLintProject(myProject, project); + if (module == null) { + return Collections.emptyList(); + } final VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(false); final List result = new ArrayList(sourceRoots.length); diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintInspectionBase.java b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintInspectionBase.java index 5cc3fe5fa98d..dfec9b262f16 100644 --- a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintInspectionBase.java +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintInspectionBase.java @@ -5,11 +5,13 @@ import com.android.tools.lint.detector.api.Issue; import com.android.tools.lint.detector.api.Severity; import com.intellij.analysis.AnalysisScope; import com.intellij.codeHighlighting.HighlightDisplayLevel; +import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; @@ -34,12 +36,12 @@ import java.util.Map; /** * @author Eugene.Kudelevsky */ -abstract class AndroidLintInspectionBase extends GlobalInspectionTool { +public abstract class AndroidLintInspectionBase extends GlobalInspectionTool { private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.AndroidLintInspectionBase"); private static final Map ourIssue2InspectionShortName = new HashMap(); - private final Issue myIssue; + protected final Issue myIssue; private final String[] myGroupPath; private final String myDisplayName; @@ -58,6 +60,29 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool { addIssue(issue, getShortName()); } + @NotNull + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return AndroidLintQuickFix.EMPTY_ARRAY; + } + + @NotNull + protected IntentionAction[] getIntentions(@NotNull PsiElement startElement, @NotNull PsiElement endElement) { + return IntentionAction.EMPTY_ARRAY; + } + + @NotNull + private LocalQuickFix[] getLocalQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { + final AndroidLintQuickFix[] fixes = getQuickFixes(message); + final LocalQuickFix[] result = new LocalQuickFix[fixes.length]; + + for (int i = 0; i < fixes.length; i++) { + if (fixes[i].isApplicable(startElement, endElement, true)) { + result[i] = new MyLocalQuickFix(fixes[i]); + } + } + return result; + } + @Override public void runInspection(AnalysisScope scope, final InspectionManager manager, @@ -81,7 +106,7 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool { for (final Map.Entry> entry : file2ProblemList.entrySet()) { final File file = entry.getKey(); final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file); - + if (vFile == null) { continue; } @@ -104,9 +129,9 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool { } @NotNull - private static ProblemDescriptor[] computeProblemDescriptors(@NotNull PsiFile psiFile, - @NotNull InspectionManager manager, - @NotNull List problems) { + private ProblemDescriptor[] computeProblemDescriptors(@NotNull PsiFile psiFile, + @NotNull InspectionManager manager, + @NotNull List problems) { final List result = new ArrayList(); for (ProblemData problemData : problems) { @@ -128,9 +153,9 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool { } } } - + if (f != null) { - result.add(manager.createProblemDescriptor(f, message, false, LocalQuickFix.EMPTY_ARRAY, + result.add(manager.createProblemDescriptor(f, message, false, getLocalQuickFixes(f, f, message), ProblemHighlightType.GENERIC_ERROR_OR_WARNING)); } } @@ -140,7 +165,8 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool { if (startElement != null && endElement != null) { result.add(manager.createProblemDescriptor(startElement, endElement, message + "#loc", - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false)); + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false, + getLocalQuickFixes(startElement, endElement, message))); } } } @@ -218,7 +244,7 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool { @Override public boolean isEnabledByDefault() { - return true; + return myIssue.isEnabledByDefault(); } @NotNull @@ -254,4 +280,29 @@ abstract class AndroidLintInspectionBase extends GlobalInspectionTool { return null; } } + + static class MyLocalQuickFix implements LocalQuickFix { + private final AndroidLintQuickFix myLintQuickFix; + + MyLocalQuickFix(@NotNull AndroidLintQuickFix lintQuickFix) { + myLintQuickFix = lintQuickFix; + } + + @NotNull + @Override + public String getName() { + return myLintQuickFix.getName(); + } + + @NotNull + @Override + public String getFamilyName() { + return AndroidBundle.message("android.lint.quickfixes.family"); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + myLintQuickFix.apply(descriptor.getStartElement(), descriptor.getEndElement(), null); + } + } } diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintInspectionToolProvider.java b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintInspectionToolProvider.java index 2c44d975a613..8beeef803494 100644 --- a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintInspectionToolProvider.java +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintInspectionToolProvider.java @@ -1,7 +1,13 @@ package org.jetbrains.android.inspections.lint; import com.android.tools.lint.checks.*; +import com.android.tools.lint.detector.api.Issue; +import com.android.tools.lint.detector.api.LintConstants; +import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.InspectionToolProvider; +import com.intellij.psi.PsiElement; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NotNull; /** * @author Eugene.Kudelevsky @@ -9,11 +15,11 @@ import com.intellij.codeInspection.InspectionToolProvider; public class AndroidLintInspectionToolProvider implements InspectionToolProvider { @Override public Class[] getInspectionClasses() { - return new Class[] { + return new Class[]{ AndroidLintContentDescriptionInspection.class, AndroidLintAdapterViewChildrenInspection.class, AndroidLintScrollViewCountInspection.class, - AndroidLintDeprecatedInspection.class, + //AndroidLintDeprecatedInspection.class, AndroidLintMissingPrefixInspection.class, AndroidLintDuplicateIdsInspection.class, AndroidLintGridLayoutInspection.class, @@ -169,29 +175,44 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider public AndroidLintContentDescriptionInspection() { super("Missing content description", AccessibilityDetector.ISSUE); } + + @NotNull + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{ + new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.add.content.description"), + LintConstants.ATTR_CONTENT_DESCRIPTION, null) + }; + } } public static class AndroidLintAdapterViewChildrenInspection extends AndroidLintInspectionBase { public AndroidLintAdapterViewChildrenInspection() { - super("AdapterView element cannot have children", ChildCountDetector.ADAPTERVIEW_ISSUE); + super(AndroidBundle.message("android.lint.inspections.adapter.view.children"), ChildCountDetector.ADAPTERVIEW_ISSUE); } } public static class AndroidLintScrollViewCountInspection extends AndroidLintInspectionBase { public AndroidLintScrollViewCountInspection() { - super("ScrollView element can have only one child", ChildCountDetector.SCROLLVIEW_ISSUE); + super(AndroidBundle.message("android.lint.inspections.scroll.view.children"), ChildCountDetector.SCROLLVIEW_ISSUE); } } - public static class AndroidLintDeprecatedInspection extends AndroidLintInspectionBase { + // it seems we don't need it because we have our own 'deprecated api' inspection + /*public static class AndroidLintDeprecatedInspection extends AndroidLintInspectionBase { public AndroidLintDeprecatedInspection() { - super("Deprecated XML elements", DeprecationDetector.ISSUE); + super(AndroidBundle.message("android.lint.inspections.deprecated"), DeprecationDetector.ISSUE); } - } + }*/ public static class AndroidLintMissingPrefixInspection extends AndroidLintInspectionBase { public AndroidLintMissingPrefixInspection() { - super("Missing android XML namespace", DetectMissingPrefix.MISSING_NAMESPACE); + super(AndroidBundle.message("android.lint.inspections.missing.prefix"), DetectMissingPrefix.MISSING_NAMESPACE); + } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{new AddMissingPrefixQuickFix()}; } } @@ -211,12 +232,26 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider public AndroidLintHardcodedTextInspection() { super("Hardcoded text", HardcodedValuesDetector.ISSUE); } + + @NotNull + @Override + protected IntentionAction[] getIntentions(@NotNull final PsiElement startElement, @NotNull PsiElement endElement) { + return new IntentionAction[]{new AndroidAddStringResourceQuickFix(startElement)}; + } } public static class AndroidLintInefficientWeightInspection extends AndroidLintInspectionBase { public AndroidLintInefficientWeightInspection() { super("Inefficient layout weight", InefficientWeightDetector.INEFFICIENT_WEIGHT); } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{ + new InefficientWeightQuickFix() + }; + } } public static class AndroidLintNestedWeightsInspection extends AndroidLintInspectionBase { @@ -229,6 +264,15 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider public AndroidLintDisableBaselineAlignmentInspection() { super("Missing baselineAligned attribute", InefficientWeightDetector.BASELINE_WEIGHTS); } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{ + new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.set.baseline.attribute"), + LintConstants.ATTR_BASELINE_ALIGNED, "false") + }; + } } public static class AndroidLintManifestOrderInspection extends AndroidLintInspectionBase { @@ -253,6 +297,12 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider public AndroidLintObsoleteLayoutParamInspection() { super("Obsolete layout params", ObsoleteLayoutParamsDetector.ISSUE); } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{new RemoveAttributeQuickFix()}; + } } public static class AndroidLintProguardInspection extends AndroidLintInspectionBase { @@ -265,18 +315,39 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider public AndroidLintPxUsageInspection() { super("Using 'px' dimension", PxUsageDetector.ISSUE); } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{new ConvertToDpQuickFix()}; + } } public static class AndroidLintScrollViewSizeInspection extends AndroidLintInspectionBase { public AndroidLintScrollViewSizeInspection() { super("ScrollView size validation", ScrollViewChildDetector.ISSUE); } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{new SetScrollViewSizeQuickFix()}; + } } public static class AndroidLintExportedServiceInspection extends AndroidLintInspectionBase { public AndroidLintExportedServiceInspection() { super("Exported service does not require permission", SecurityDetector.EXPORTED_SERVICE); } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{ + new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.add.permission.attribute"), + LintConstants.ATTR_PERMISSION, null) + }; + } } public static class AndroidLintGrantAllUrisInspection extends AndroidLintInspectionBase { @@ -295,6 +366,15 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider public AndroidLintTextFieldsInspection() { super("Text field missing inputType or hint settings", TextFieldDetector.ISSUE); } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{ + new SetAttributeQuickFix(AndroidBundle.message("android.lint.inspections.add.input.type.attribute"), + LintConstants.ATTR_INPUT_TYPE, null) + }; + } } public static class AndroidLintTooManyViewsInspection extends AndroidLintInspectionBase { @@ -309,31 +389,31 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider } } - public static class AndroidLintTypographyDashesInspection extends AndroidLintInspectionBase { + public static class AndroidLintTypographyDashesInspection extends AndroidLintTypographyInspectionBase { public AndroidLintTypographyDashesInspection() { super("Hyphen can be replaced by dash", TypographyDetector.DASHES); } } - public static class AndroidLintTypographyQuotesInspection extends AndroidLintInspectionBase { + public static class AndroidLintTypographyQuotesInspection extends AndroidLintTypographyInspectionBase { public AndroidLintTypographyQuotesInspection() { super("Straight quotes can be replaced by curvy quotes", TypographyDetector.QUOTES); } } - public static class AndroidLintTypographyFractionsInspection extends AndroidLintInspectionBase { + public static class AndroidLintTypographyFractionsInspection extends AndroidLintTypographyInspectionBase { public AndroidLintTypographyFractionsInspection() { super("Fraction string can be replaced with fraction character", TypographyDetector.FRACTIONS); } } - public static class AndroidLintTypographyEllipsisInspection extends AndroidLintInspectionBase { + public static class AndroidLintTypographyEllipsisInspection extends AndroidLintTypographyInspectionBase { public AndroidLintTypographyEllipsisInspection() { super("Ellipsis string can be replaced with ellipsis character", TypographyDetector.ELLIPSIS); } } - public static class AndroidLintTypographyOtherInspection extends AndroidLintInspectionBase { + public static class AndroidLintTypographyOtherInspection extends AndroidLintTypographyInspectionBase { public AndroidLintTypographyOtherInspection() { super("Other typographical problems", TypographyDetector.OTHER); } @@ -355,5 +435,23 @@ public class AndroidLintInspectionToolProvider implements InspectionToolProvider public AndroidLintUselessLeafInspection() { super("Useless leaf layout", UselessViewDetector.USELESS_LEAF); } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[]{new RemoveUselessViewQuickFix(myIssue)}; + } + } + + private static class AndroidLintTypographyInspectionBase extends AndroidLintInspectionBase { + public AndroidLintTypographyInspectionBase(String displayName, Issue issue) { + super(displayName, issue); + } + + @NotNull + @Override + protected AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { + return new AndroidLintQuickFix[] {new TypographyQuickFix(myIssue, message)}; + } } } diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintQuickFix.java new file mode 100644 index 000000000000..f029cb923057 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintQuickFix.java @@ -0,0 +1,20 @@ +package org.jetbrains.android.inspections.lint; + +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +interface AndroidLintQuickFix { + AndroidLintQuickFix[] EMPTY_ARRAY = new AndroidLintQuickFix[0]; + + void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor); + + boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode); + + @NotNull + String getName(); +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintUtil.java b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintUtil.java new file mode 100644 index 000000000000..95767ccf261b --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/AndroidLintUtil.java @@ -0,0 +1,57 @@ +package org.jetbrains.android.inspections.lint; + +import com.android.tools.lint.detector.api.Issue; +import com.intellij.codeHighlighting.HighlightDisplayLevel; +import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInspection.InspectionProfile; +import com.intellij.codeInspection.ex.InspectionToolWrapper; +import com.intellij.openapi.util.Pair; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +class AndroidLintUtil { + @NonNls static final String ATTR_VALUE_VERTICAL = "vertical"; + @NonNls static final String ATTR_VALUE_WRAP_CONTENT = "wrap_content"; + @NonNls static final String ATTR_LAYOUT_HEIGHT = "layout_height"; + @NonNls static final String ATTR_LAYOUT_WIDTH = "layout_width"; + @NonNls static final String ATTR_ORIENTATION = "orientation"; + + private AndroidLintUtil() { + } + + @Nullable + static Pair getHighlighLevelAndInspection(@NotNull Issue issue, + @NotNull PsiElement context) { + final String inspectionShortName = AndroidLintInspectionBase.getInspectionShortNameByIssue(issue); + if (inspectionShortName == null) { + return null; + } + + final HighlightDisplayKey key = HighlightDisplayKey.find(inspectionShortName); + if (key == null) { + return null; + } + + final InspectionProfile profile = InspectionProjectProfileManager.getInstance(context.getProject()).getInspectionProfile(); + if (!profile.isToolEnabled(key, context)) { + return null; + } + + final InspectionToolWrapper toolWrapper = + (InspectionToolWrapper)profile.getInspectionTool(inspectionShortName, context); + if (toolWrapper == null) { + return null; + } + + final AndroidLintInspectionBase inspection = (AndroidLintInspectionBase)toolWrapper.getTool(); + final HighlightDisplayLevel errorLevel = profile.getErrorLevel(key, context); + return new Pair(inspection, + errorLevel != null ? errorLevel : HighlightDisplayLevel.WARNING); + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/ConvertToDpQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/ConvertToDpQuickFix.java new file mode 100644 index 000000000000..e0190e1bc360 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/ConvertToDpQuickFix.java @@ -0,0 +1,127 @@ +package org.jetbrains.android.inspections.lint; + +import com.android.resources.Density; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.ui.Messages; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Eugene.Kudelevsky + */ +class ConvertToDpQuickFix implements AndroidLintQuickFix { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.ConvertToDpQuickFix"); + private static final Pattern PX_ATTR_VALUE_PATTERN = Pattern.compile("(\\d+)px"); + + private static int ourPrevDpi = Density.DEFAULT_DENSITY; + + @Override + public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) { + if (editor == null) { + return; + } + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class); + final List densities = new ArrayList(); + + for (Density density : Density.values()) { + if (density.getDpiValue() > 0) { + densities.add(density); + } + } + + final String[] densityPresentableNames = new String[densities.size()]; + + String defaultValue = null; + String initialValue = null; + + for (int i = 0; i < densities.size(); i++) { + final Density density = densities.get(i); + densityPresentableNames[i] = getLabelForDensity(density); + + final int dpi = density.getDpiValue(); + if (dpi == 0) { + continue; + } + + if (dpi == ourPrevDpi) { + initialValue = densityPresentableNames[i]; + } + else if (dpi == Density.DEFAULT_DENSITY) { + defaultValue = densityPresentableNames[i]; + } + } + + if (initialValue == null) { + initialValue = defaultValue; + } + if (initialValue == null) { + return; + } + + final int dpi; + + if (ApplicationManager.getApplication().isUnitTestMode()) { + dpi = Density.DEFAULT_DENSITY; + } + else { + final int selectedIndex = Messages + .showChooseDialog("What is the screen density the current px value works with?", "Choose density", densityPresentableNames, + initialValue, null); + if (selectedIndex < 0) { + return; + } + dpi = densities.get(selectedIndex).getDpiValue(); + } + + //noinspection AssignmentToStaticFieldFromInstanceMethod + ourPrevDpi = dpi; + + for (XmlAttribute attribute : tag.getAttributes()) { + final String value = attribute.getValue(); + + if (value.endsWith("px")) { + final Matcher matcher = PX_ATTR_VALUE_PATTERN.matcher(value); + + if (matcher.matches()) { + final String numberString = matcher.group(1); + try { + final int px = Integer.parseInt(numberString); + final int dp = px * 160 / dpi; + attribute.setValue(Integer.toString(dp) + "dp"); + } + catch (NumberFormatException nufe) { + LOG.error(nufe); + } + } + } + } + } + + @NotNull + private static String getLabelForDensity(@NotNull Density density) { + return String.format("%1$s (%2$d)", density.getShortDisplayValue(), density.getDpiValue()); + } + + @Override + public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) { + return !inBatchMode && PsiTreeUtil.getParentOfType(startElement, XmlTag.class) != null; + } + + @NotNull + @Override + public String getName() { + return AndroidBundle.message("android.lint.inspections.convert.to.dp"); + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/FakeNode.java b/plugins/android/src/org/jetbrains/android/inspections/lint/FakeNode.java new file mode 100644 index 000000000000..8504add151a7 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/FakeNode.java @@ -0,0 +1,200 @@ +package org.jetbrains.android.inspections.lint; + +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.*; + +/** + * @author Eugene.Kudelevsky + */ +class FakeNode implements Node { + private final String myText; + + FakeNode(@NotNull String text) { + myText = text; + } + + @Override + public String getNodeName() { + throw new UnsupportedOperationException(); + } + + @Override + public String getNodeValue() throws DOMException { + return myText; + } + + @Override + public void setNodeValue(String nodeValue) throws DOMException { + throw new UnsupportedOperationException(); + } + + @Override + public short getNodeType() { + throw new UnsupportedOperationException(); + } + + @Override + public Node getParentNode() { + throw new UnsupportedOperationException(); + } + + @Override + public NodeList getChildNodes() { + throw new UnsupportedOperationException(); + } + + @Override + public Node getFirstChild() { + throw new UnsupportedOperationException(); + } + + @Override + public Node getLastChild() { + throw new UnsupportedOperationException(); + } + + @Override + public Node getPreviousSibling() { + throw new UnsupportedOperationException(); + } + + @Override + public Node getNextSibling() { + throw new UnsupportedOperationException(); + } + + @Override + public NamedNodeMap getAttributes() { + throw new UnsupportedOperationException(); + } + + @Override + public Document getOwnerDocument() { + throw new UnsupportedOperationException(); + } + + @Override + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + throw new UnsupportedOperationException(); + } + + @Override + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + throw new UnsupportedOperationException(); + } + + @Override + public Node removeChild(Node oldChild) throws DOMException { + throw new UnsupportedOperationException(); + } + + @Override + public Node appendChild(Node newChild) throws DOMException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasChildNodes() { + throw new UnsupportedOperationException(); + } + + @Override + public Node cloneNode(boolean deep) { + throw new UnsupportedOperationException(); + } + + @Override + public void normalize() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSupported(String feature, String version) { + throw new UnsupportedOperationException(); + } + + @Override + public String getNamespaceURI() { + throw new UnsupportedOperationException(); + } + + @Override + public String getPrefix() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPrefix(String prefix) throws DOMException { + throw new UnsupportedOperationException(); + } + + @Override + public String getLocalName() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasAttributes() { + throw new UnsupportedOperationException(); + } + + @Override + public String getBaseURI() { + throw new UnsupportedOperationException(); + } + + @Override + public short compareDocumentPosition(Node other) throws DOMException { + throw new UnsupportedOperationException(); + } + + @Override + public String getTextContent() throws DOMException { + throw new UnsupportedOperationException(); + } + + @Override + public void setTextContent(String textContent) throws DOMException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSameNode(Node other) { + throw new UnsupportedOperationException(); + } + + @Override + public String lookupPrefix(String namespaceURI) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDefaultNamespace(String namespaceURI) { + throw new UnsupportedOperationException(); + } + + @Override + public String lookupNamespaceURI(String prefix) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEqualNode(Node arg) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getFeature(String feature, String version) { + throw new UnsupportedOperationException(); + } + + @Override + public Object setUserData(String key, Object data, UserDataHandler handler) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getUserData(String key) { + throw new UnsupportedOperationException(); + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/InefficientWeightQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/InefficientWeightQuickFix.java new file mode 100644 index 000000000000..7d2c4e6ee443 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/InefficientWeightQuickFix.java @@ -0,0 +1,54 @@ +package org.jetbrains.android.inspections.lint; + +import com.android.sdklib.SdkConstants; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +class InefficientWeightQuickFix implements AndroidLintQuickFix { + + @Override + public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) { + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class); + if (tag == null) { + return; + } + + final XmlTag parentTag = tag.getParentTag(); + if (parentTag == null) { + return; + } + String attrName; + + if (AndroidLintUtil.ATTR_VALUE_VERTICAL + .equals(parentTag.getAttributeValue(AndroidLintUtil.ATTR_ORIENTATION, SdkConstants.NS_RESOURCES))) { + attrName = AndroidLintUtil.ATTR_LAYOUT_HEIGHT; + } + else { + attrName = AndroidLintUtil.ATTR_LAYOUT_WIDTH; + } + tag.setAttribute(attrName, SdkConstants.NS_RESOURCES, "0dp"); + } + + @Override + public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) { + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class); + if (tag == null) { + return false; + } + return tag.getParentTag() != null; + } + + @NotNull + @Override + public String getName() { + return AndroidBundle.message("android.lint.inspections.replace.with.zero.dp"); + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/IntellijLintClient.java b/plugins/android/src/org/jetbrains/android/inspections/lint/IntellijLintClient.java index cab3907e7252..528970c87f64 100644 --- a/plugins/android/src/org/jetbrains/android/inspections/lint/IntellijLintClient.java +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/IntellijLintClient.java @@ -1,6 +1,7 @@ package org.jetbrains.android.inspections.lint; import com.android.tools.lint.PositionXmlParser; +import com.android.tools.lint.client.api.Configuration; import com.android.tools.lint.client.api.IDomParser; import com.android.tools.lint.client.api.LintClient; import com.android.tools.lint.detector.api.Context; @@ -43,6 +44,11 @@ class IntellijLintClient extends LintClient implements Disposable { myState = state; } + @Override + public Configuration getConfiguration(com.android.tools.lint.detector.api.Project project) { + return new IntellijLintConfiguration(myState.getIssues()); + } + @Override public void report(Context context, Issue issue, Location location, String message, Object data) { if (location != null) { diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/IntellijLintConfiguration.java b/plugins/android/src/org/jetbrains/android/inspections/lint/IntellijLintConfiguration.java new file mode 100644 index 000000000000..d24cede931b2 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/IntellijLintConfiguration.java @@ -0,0 +1,43 @@ +package org.jetbrains.android.inspections.lint; + +import com.android.tools.lint.client.api.Configuration; +import com.android.tools.lint.detector.api.Context; +import com.android.tools.lint.detector.api.Issue; +import com.android.tools.lint.detector.api.Location; +import com.android.tools.lint.detector.api.Severity; +import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Set; + +/** + * @author Eugene.Kudelevsky + */ +class IntellijLintConfiguration extends Configuration { + private final Set myIssueSet; + + IntellijLintConfiguration(@NotNull Collection issueSet) { + myIssueSet = new HashSet(issueSet); + } + + @Override + public void ignore(Context context, Issue issue, Location location, String message, Object data) { + throw new UnsupportedOperationException(); + } + + @Override + public void setSeverity(Issue issue, Severity severity) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEnabled(Issue issue) { + return myIssueSet.contains(issue); + } + + @Override + public Severity getSeverity(Issue issue) { + return Severity.WARNING; + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/RemoveAttributeQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/RemoveAttributeQuickFix.java new file mode 100644 index 000000000000..b9d01eaba5e9 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/RemoveAttributeQuickFix.java @@ -0,0 +1,33 @@ +package org.jetbrains.android.inspections.lint; + +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlAttribute; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +class RemoveAttributeQuickFix implements AndroidLintQuickFix { + @Override + public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) { + final XmlAttribute attribute = PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class); + if (attribute != null) { + attribute.getParent().setAttribute(attribute.getName(), null); + } + } + + @Override + public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) { + return PsiTreeUtil.getParentOfType(startElement, XmlAttribute.class) != null; + } + + @NotNull + @Override + public String getName() { + return AndroidBundle.message("android.lint.inspections.remove.attribute"); + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/RemoveUselessViewQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/RemoveUselessViewQuickFix.java new file mode 100644 index 000000000000..3c429b7fe435 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/RemoveUselessViewQuickFix.java @@ -0,0 +1,58 @@ +package org.jetbrains.android.inspections.lint; + +import com.android.tools.lint.checks.UselessViewDetector; +import com.android.tools.lint.detector.api.Issue; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +public class RemoveUselessViewQuickFix implements AndroidLintQuickFix { + private final Issue myIssue; + + public RemoveUselessViewQuickFix(@NotNull Issue issue) { + myIssue = issue; + } + + @Override + public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) { + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class); + if (tag == null) { + return; + } + + final XmlTag parentTag = tag.getParentTag(); + if (parentTag == null) { + return; + } + + if (myIssue.getId().equals(UselessViewDetector.USELESS_LEAF.getId())) { + tag.delete(); + } + else { + assert false; + // todo: implement + } + } + + @Override + public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) { + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class); + if (tag == null) { + return false; + } + return tag.getParentTag() != null; + } + + @NotNull + @Override + public String getName() { + return AndroidBundle.message("android.lint.inspections.remove.unnecessary.view"); + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/SetAttributeQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/SetAttributeQuickFix.java new file mode 100644 index 000000000000..4143549ef208 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/SetAttributeQuickFix.java @@ -0,0 +1,74 @@ +package org.jetbrains.android.inspections.lint; + +import com.android.sdklib.SdkConstants; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +class SetAttributeQuickFix implements AndroidLintQuickFix { + + private final String myName; + private final String myAttributeName; + private final String myValue; + + SetAttributeQuickFix(@NotNull String name, @NotNull String attributeName, @Nullable String value) { + super(); + myName = name; + myAttributeName = attributeName; + myValue = value; + } + + @NotNull + @Override + public String getName() { + return myName; + } + + @Override + public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) { + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class, false); + + if (tag == null) { + return; + } + final XmlAttribute attribute = tag.setAttribute(myAttributeName, SdkConstants.NS_RESOURCES, ""); + + if (attribute != null) { + if (myValue != null) { + attribute.setValue(myValue); + } + if (editor != null) { + final XmlAttributeValue valueElement = attribute.getValueElement(); + final TextRange valueTextRange = attribute.getValueTextRange(); + + if (valueElement != null && valueTextRange != null) { + final int valueElementStart = valueElement.getTextRange().getStartOffset(); + editor.getCaretModel().moveToOffset(valueElementStart + valueTextRange.getStartOffset()); + + if (valueTextRange.getStartOffset() < valueTextRange.getEndOffset()) { + editor.getSelectionModel().setSelection(valueElementStart + valueTextRange.getStartOffset(), + valueElementStart + valueTextRange.getEndOffset()); + } + } + } + } + } + + @Override + public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) { + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class, false); + if (tag == null) { + return false; + } + return tag.getAttribute(myAttributeName, SdkConstants.NS_RESOURCES) == null; + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/SetScrollViewSizeQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/SetScrollViewSizeQuickFix.java new file mode 100644 index 000000000000..336bb9a11f61 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/SetScrollViewSizeQuickFix.java @@ -0,0 +1,50 @@ +package org.jetbrains.android.inspections.lint; + +import com.android.sdklib.SdkConstants; +import com.android.tools.lint.detector.api.LintConstants; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene.Kudelevsky + */ +class SetScrollViewSizeQuickFix implements AndroidLintQuickFix { + @Override + public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) { + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class); + if (tag == null) { + return; + } + + final XmlTag parentTag = tag.getParentTag(); + if (parentTag == null) { + return; + } + + final boolean isHorizontal = LintConstants.HORIZONTAL_SCROLL_VIEW.equals(parentTag.getName()); + final String attributeName = isHorizontal + ? AndroidLintUtil.ATTR_LAYOUT_WIDTH + : AndroidLintUtil.ATTR_LAYOUT_HEIGHT; + tag.setAttribute(attributeName, SdkConstants.NS_RESOURCES, AndroidLintUtil.ATTR_VALUE_WRAP_CONTENT); + } + + @Override + public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) { + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class); + if (tag == null) { + return false; + } + return tag.getParentTag() != null; + } + + @NotNull + @Override + public String getName() { + return AndroidBundle.message("android.lint.inspections.set.to.wrap.content"); + } +} diff --git a/plugins/android/src/org/jetbrains/android/inspections/lint/TypographyQuickFix.java b/plugins/android/src/org/jetbrains/android/inspections/lint/TypographyQuickFix.java new file mode 100644 index 000000000000..74e7e737cdc8 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/inspections/lint/TypographyQuickFix.java @@ -0,0 +1,67 @@ +package org.jetbrains.android.inspections.lint; + +import com.android.tools.lint.checks.TypographyDetector; +import com.android.tools.lint.detector.api.Issue; +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.xml.XmlText; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Eugene.Kudelevsky + */ +class TypographyQuickFix implements AndroidLintQuickFix { + private final Issue myIssue; + private final String myMessage; + + public TypographyQuickFix(@NotNull Issue issue, @NotNull String message) { + myIssue = issue; + myMessage = message; + } + + @Override + public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @Nullable Editor editor) { + final XmlTag tag = PsiTreeUtil.getParentOfType(startElement, XmlTag.class); + if (tag == null) { + return; + } + + for (PsiElement child : tag.getChildren()) { + if (child instanceof XmlText) { + final XmlText xmlText = (XmlText)child; + final String value = xmlText.getValue(); + + if (value != null) { + final List edits = TypographyDetector.getEdits(myIssue.getId(), myMessage, new FakeNode(value)); + final StringBuilder builder = new StringBuilder(value); + + for (TypographyDetector.ReplaceEdit edit : edits) { + builder.replace(edit.offset, edit.offset + edit.length, edit.replaceWith); + } + + final String newValue = builder.toString(); + if (!newValue.equals(value)) { + xmlText.setValue(newValue); + } + } + } + } + } + + @Override + public boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, boolean inBatchMode) { + return PsiTreeUtil.getParentOfType(startElement, XmlTag.class) != null; + } + + @NotNull + @Override + public String getName() { + return AndroidBundle.message("android.lint.inspections.replace.with.suggested.characters"); + } +} diff --git a/plugins/android/src/org/jetbrains/android/intentions/AndroidAddStringResourceAction.java b/plugins/android/src/org/jetbrains/android/intentions/AndroidAddStringResourceAction.java index 9826b6d9aabe..08e641d87637 100644 --- a/plugins/android/src/org/jetbrains/android/intentions/AndroidAddStringResourceAction.java +++ b/plugins/android/src/org/jetbrains/android/intentions/AndroidAddStringResourceAction.java @@ -16,42 +16,57 @@ package org.jetbrains.android.intentions; +import com.android.resources.ResourceType; import com.intellij.CommonBundle; import com.intellij.codeInsight.intention.AbstractIntentionAction; +import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.codeInsight.template.*; import com.intellij.codeInsight.template.impl.*; import com.intellij.codeInsight.template.macro.VariableOfTypeMacro; +import com.intellij.ide.actions.CreateElementActionBase; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.undo.UndoUtil; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.InputValidatorEx; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlAttributeValue; +import com.intellij.psi.xml.XmlFile; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.xml.Converter; +import com.intellij.util.xml.DomManager; +import com.intellij.util.xml.GenericAttributeValue; +import org.jetbrains.android.AndroidFileTemplateProvider; +import org.jetbrains.android.actions.CreateXmlResourceDialog; +import org.jetbrains.android.dom.converters.ResourceReferenceConverter; import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.dom.resources.ResourceElement; +import org.jetbrains.android.dom.resources.ResourceValue; +import org.jetbrains.android.dom.resources.Resources; import org.jetbrains.android.facet.AndroidFacet; -import org.jetbrains.android.resourceManagers.LocalResourceManager; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.util.LinkedHashSet; import java.util.Set; -import static com.intellij.openapi.ui.Messages.showInputDialog; import static org.jetbrains.android.util.AndroidUtils.VIEW_CLASS_NAME; /** @@ -61,7 +76,9 @@ import static org.jetbrains.android.util.AndroidUtils.VIEW_CLASS_NAME; * Time: 5:02:31 PM * To change this template use File | Settings | File Templates. */ -public class AndroidAddStringResourceAction extends AbstractIntentionAction { +public class AndroidAddStringResourceAction extends AbstractIntentionAction implements HighPriorityAction { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.intentions.AndroidAddStringResourceAction"); + private static final String CONTEXT = AndroidUtils.ANDROID_PACKAGE + ".content.Context"; private static final String RESOURCES = AndroidUtils.ANDROID_PACKAGE + ".content.res.Resources"; @@ -94,6 +111,31 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction { return (String)value; } } + else if (file instanceof XmlFile && element instanceof XmlAttributeValue) { + final XmlAttribute attribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class); + + if (attribute != null) { + final GenericAttributeValue domAttribute = DomManager.getDomManager(element.getProject()).getDomElement(attribute); + + if (domAttribute != null) { + final Converter converter = domAttribute.getConverter(); + + if (converter instanceof ResourceReferenceConverter) { + final ResourceValue value = (ResourceValue)domAttribute.getValue(); + + if (value != null && !value.isReference()) { + final Set types = ((ResourceReferenceConverter)converter).getResourceTypes(domAttribute); + + for (String type : types) { + if (ResourceType.STRING.getName().equals(type)) { + return ((XmlAttributeValue)element).getValue(); + } + } + } + } + } + } + } return null; } @@ -124,50 +166,70 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction { } static void doInvoke(Project project, Editor editor, PsiFile file, @Nullable String resName) { - AndroidFacet facet = AndroidFacet.getInstance(file); - assert facet != null; - PsiElement element = getPsiElement(file, editor); + final PsiElement element = getPsiElement(file, editor); assert element != null; + + doInvoke(project, editor, file, resName, element); + } + + protected static void doInvoke(Project project, Editor editor, PsiFile file, @Nullable String resName, PsiElement element) { String value = getStringLiteralValue(element, file); assert value != null; + + final AndroidFacet facet = AndroidFacet.getInstance(file); + assert facet != null; + value = value.replace("'", "\\'").replace("\"", "\\\""); - String aPackage = getPackage(facet); + + final String aPackage = getPackage(facet); if (aPackage == null) { Messages.showErrorDialog(project, AndroidBundle.message("package.not.found.error"), CommonBundle.getErrorTitle()); return; } + if (resName == null) { - resName = - showInputDialog(project, AndroidBundle.message("resource.name"), AndroidBundle.message("add.string.resource.intention.text"), - Messages.getQuestionIcon(), "", new InputValidatorEx() { - public String getErrorText(String inputString) { - if (inputString == null || inputString.length() == 0) return null; - String[] ids = inputString.split("."); - for (String id : ids) { - if (!StringUtil.isJavaIdentifier(id)) { - return AndroidBundle.message("android.identifier.expected", id); - } - } - return null; - } + final CreateXmlResourceDialog dialog = new CreateXmlResourceDialog(facet.getModule(), ResourceType.STRING); + dialog.setTitle("Extract String Resource"); + dialog.show(); - public boolean checkInput(String inputString) { - return inputString != null && AndroidResourceUtil.isCorrectAndroidResourceName(inputString); - } + if (!dialog.isOK()) { + return; + } - public boolean canClose(String inputString) { - return checkInput(inputString); - } - }); + final Module module = dialog.getModule(); + if (module == null) { + return; + } + + final ResourceElement createdElement = + doCreate(module, dialog.getResourceName(), ResourceType.STRING, dialog.getFileName(), dialog.getDirectoryName(), value); + + if (createdElement != null) { + resName = createdElement.getName().getValue(); + } } - if (resName == null) return; - LocalResourceManager manager = facet.getLocalResourceManager(); - String resType = "string"; - ResourceElement resElement = manager.addValueResource(resType, resName, value); - assert resElement != null; - createJavaResourceReference(project, editor, file, element, aPackage, resName, resType); + else { + assert ApplicationManager.getApplication().isUnitTestMode(); + doCreate(facet.getModule(), resName, ResourceType.STRING, "strings.xml", "values", value); + } + + if (resName == null) { + return; + } + + if (file instanceof PsiJavaFile) { + createJavaResourceReference(project, editor, file, element, aPackage, resName, ResourceType.STRING.getName()); + } + else { + final XmlAttribute attribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class); + if (attribute != null) { + attribute.setValue(ResourceValue.referenceTo('@', null, ResourceType.STRING.getName(), resName).toString()); + } + } + PsiDocumentManager.getInstance(project).commitAllDocuments(); UndoUtil.markPsiFileForUndo(file); + ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { ApplicationManager.getApplication().saveAll(); @@ -188,14 +250,25 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction { assert methodName != null; final TemplateImpl template; final boolean inStaticContext = RefactoringUtil.isInStaticContext(element, null); + if (extendsContext && !inStaticContext) { - template = new TemplateImpl("", "$resources$." + methodName + "(" + field + ")", ""); - MacroCallNode node = new MacroCallNode(new MyVarOfTypeExpression("getResources()")); - node.addParameter(new ConstantNode(RESOURCES)); - template.addVariable("resources", node, new ConstantNode(""), true); + if (ResourceType.STRING.getName().equals(resType)) { + template = new TemplateImpl("", methodName + '(' + field + ')', ""); + } + else { + template = new TemplateImpl("", "$resources$." + methodName + "(" + field + ")", ""); + MacroCallNode node = new MacroCallNode(new MyVarOfTypeExpression("getResources()")); + node.addParameter(new ConstantNode(RESOURCES)); + template.addVariable("resources", node, new ConstantNode(""), true); + } } else { - template = new TemplateImpl("", "$context$.getResources()." + methodName + "(" + field + ")", ""); + if (ResourceType.STRING.getName().equals(resType)) { + template = new TemplateImpl("", "$context$." + methodName + "(" + field + ")", ""); + } + else { + template = new TemplateImpl("", "$context$.getResources()." + methodName + "(" + field + ")", ""); + } final boolean extendsView = getContainingInheritorOf(element, VIEW_CLASS_NAME) != null; MacroCallNode node = new MacroCallNode(extendsView && !inStaticContext ? new MyVarOfTypeExpression("getContext()") : new VariableOfTypeMacro()); @@ -242,6 +315,104 @@ public class AndroidAddStringResourceAction extends AbstractIntentionAction { return true; } + @Nullable + private static ResourceElement doCreate(@NotNull Module module, + @NotNull String resourceName, + @NotNull ResourceType resourceType, + @NotNull String fileName, + @NotNull String dirName, + @NotNull String value) { + final Project project = module.getProject(); + final AndroidFacet facet = AndroidFacet.getInstance(module); + assert facet != null; + + try { + return addValueResource(facet, resourceName, resourceType, fileName, dirName, value); + } + catch (Exception e) { + final String message = CreateElementActionBase.filterMessage(e.getMessage()); + + if (message == null || message.length() == 0) { + LOG.error(e); + } + else { + LOG.info(e); + reportError(project, message); + } + return null; + } + } + + @Nullable + public static ResourceElement addValueResource(@NotNull AndroidFacet facet, + @NotNull String resourceName, + @NotNull ResourceType resourceType, + @NotNull String fileName, + @NotNull String dirName, + @NotNull String value) throws Exception { + final VirtualFile resFile = findOrCreateResourceFile(facet, fileName, dirName); + if (resFile == null) { + return null; + } + + final Resources resources = AndroidUtils.loadDomElement(facet.getModule(), resFile, Resources.class); + if (resources == null) { + reportError(facet.getModule().getProject(), AndroidBundle.message("not.resource.file.error", fileName)); + return null; + } + + final ResourceElement element = AndroidResourceUtil.addValueResource(resourceType.getName(), resources); + element.getName().setValue(resourceName); + + if (value.length() > 0) { + element.setStringValue(value); + } + return element; + } + + @Nullable + private static VirtualFile findOrCreateResourceFile(@NotNull AndroidFacet facet, + @NotNull final String fileName, + @NotNull String dirName) throws Exception { + final Module module = facet.getModule(); + final Project project = module.getProject(); + final VirtualFile resDir = facet.getLocalResourceManager().getResourceDir(); + + if (resDir == null) { + reportError(project, AndroidBundle.message("check.resource.dir.error", module.getName())); + return null; + } + final VirtualFile dir = AndroidUtils.createChildDirectoryIfNotExist(project, resDir, dirName); + final String dirPath = FileUtil.toSystemDependentName(resDir.getPath() + '/' + dirName); + + if (dir == null) { + reportError(project, AndroidBundle.message("android.cannot.create.dir.error", dirPath)); + return null; + } + + final VirtualFile file = dir.findChild(fileName); + if (file != null) { + return file; + } + + AndroidFileTemplateProvider + .createFromTemplate(project, dir, AndroidFileTemplateProvider.VALUE_RESOURCE_FILE_TEMPLATE, fileName); + final VirtualFile result = dir.findChild(fileName); + if (result == null) { + reportError(project, AndroidBundle.message("android.cannot.create.file.error", dirPath + File.separatorChar + fileName)); + } + return result; + } + + private static void reportError(@NotNull Project project, @NotNull String message) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + throw new IncorrectOperationException(message); + } + else { + Messages.showErrorDialog(project, message, CommonBundle.getErrorTitle()); + } + } + private static class MyVarOfTypeExpression extends VariableOfTypeMacro { private final String myDefaultValue; diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java index fc34714eefeb..3722ec9a371b 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java @@ -208,7 +208,8 @@ public class LocalResourceManager extends ResourceManager { private VirtualFile findOrCreateResourceFile(@NotNull final String fileName) { VirtualFile dir = getResourceDir(); if (dir == null) { - Messages.showErrorDialog(myModule.getProject(), AndroidBundle.message("check.resource.dir.error"), CommonBundle.getErrorTitle()); + Messages.showErrorDialog(myModule.getProject(), AndroidBundle.message("check.resource.dir.error", myModule.getName()), + CommonBundle.getErrorTitle()); return null; } final VirtualFile valuesDir = findOrCreateChildDir(dir, AndroidConstants.FD_RES_VALUES); @@ -240,7 +241,8 @@ public class LocalResourceManager extends ResourceManager { VirtualFile resDir = getResourceDir(); Project project = myModule.getProject(); if (resDir == null) { - Messages.showErrorDialog(project, AndroidBundle.message("check.resource.dir.error"), CommonBundle.getErrorTitle()); + Messages + .showErrorDialog(project, AndroidBundle.message("check.resource.dir.error", myModule.getName()), CommonBundle.getErrorTitle()); return null; } PsiElement[] createdElements = CreateResourceFileAction.createResourceFile(project, resDir, resType, fileOrResourceName); @@ -269,7 +271,7 @@ public class LocalResourceManager extends ResourceManager { CommonBundle.getErrorTitle()); return null; } - ResourceElement element = addValueResource(type, resources); + ResourceElement element = AndroidResourceUtil.addValueResource(type, resources); element.getName().setValue(name); if (value != null) { element.setStringValue(value); @@ -277,41 +279,6 @@ public class LocalResourceManager extends ResourceManager { return element; } - @NotNull - private static ResourceElement addValueResource(@NotNull final String type, @NotNull final Resources resources) { - if (type.equals("string")) { - return resources.addString(); - } - else if (type.equals("dimen")) { - return resources.addDimen(); - } - else if (type.equals("color")) { - return resources.addColor(); - } - else if (type.equals("drawable")) { - return resources.addDrawable(); - } - else if (type.equals("style")) { - return resources.addStyle(); - } - else if (type.equals("array")) { - // todo: choose among string-array, integer-array and array - return resources.addStringArray(); - } - else if (type.equals("integer")) { - return resources.addInteger(); - } - else if (type.equals("bool")) { - return resources.addBool(); - } - else if (type.equals("id")) { - Item item = resources.addItem(); - item.getType().setValue("id"); - return item; - } - throw new IllegalArgumentException("Incorrect resource type"); - } - @Nullable private VirtualFile findOrCreateChildDir(@NotNull final VirtualFile dir, @NotNull final String name) { try { diff --git a/plugins/android/src/org/jetbrains/android/uipreview/DeviceConfiguratorPanel.java b/plugins/android/src/org/jetbrains/android/uipreview/DeviceConfiguratorPanel.java index 19738d60d394..ebb4303166f4 100644 --- a/plugins/android/src/org/jetbrains/android/uipreview/DeviceConfiguratorPanel.java +++ b/plugins/android/src/org/jetbrains/android/uipreview/DeviceConfiguratorPanel.java @@ -252,7 +252,7 @@ public abstract class DeviceConfiguratorPanel extends JPanel { applyEditors(); } - protected void doApplyEditors() throws InvalidOptionValueException { + public void doApplyEditors() throws InvalidOptionValueException { try { final FolderConfiguration newConfig = new FolderConfiguration(); diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java index b3c0a380d7a8..4d95bb5dc5b1 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidResourceUtil.java @@ -35,9 +35,7 @@ import com.intellij.util.containers.HashSet; import com.intellij.util.indexing.FileBasedIndex; import org.jetbrains.android.AndroidIdIndex; import org.jetbrains.android.dom.manifest.Manifest; -import org.jetbrains.android.dom.resources.Attr; -import org.jetbrains.android.dom.resources.DeclareStyleable; -import org.jetbrains.android.dom.resources.ResourceElement; +import org.jetbrains.android.dom.resources.*; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.resourceManagers.LocalResourceManager; import org.jetbrains.android.resourceManagers.ResourceManager; @@ -402,4 +400,39 @@ public class AndroidResourceUtil { List targets = findResourcesByField(manager, resField); return PsiUtilBase.toPsiElementArray(targets); } + + @NotNull + public static ResourceElement addValueResource(@NotNull final String type, @NotNull final Resources resources) { + if (type.equals("string")) { + return resources.addString(); + } + else if (type.equals("dimen")) { + return resources.addDimen(); + } + else if (type.equals("color")) { + return resources.addColor(); + } + else if (type.equals("drawable")) { + return resources.addDrawable(); + } + else if (type.equals("style")) { + return resources.addStyle(); + } + else if (type.equals("array")) { + // todo: choose among string-array, integer-array and array + return resources.addStringArray(); + } + else if (type.equals("integer")) { + return resources.addInteger(); + } + else if (type.equals("bool")) { + return resources.addBool(); + } + else if (type.equals("id")) { + Item item = resources.addItem(); + item.getType().setValue("id"); + return item; + } + throw new IllegalArgumentException("Incorrect resource type"); + } } diff --git a/plugins/android/testData/addStringRes/Class10_after.java b/plugins/android/testData/addStringRes/Class10_after.java index f4fa6807e3f1..baf3c102a5e4 100644 --- a/plugins/android/testData/addStringRes/Class10_after.java +++ b/plugins/android/testData/addStringRes/Class10_after.java @@ -6,6 +6,6 @@ import p1.p2.R; public class Class extends View { public static void f(Context context) { - String s = context.getResources().getString(R.string.hello); + String s = context.getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class11_after.java b/plugins/android/testData/addStringRes/Class11_after.java index 8b6382d95d97..9382074c0fb6 100644 --- a/plugins/android/testData/addStringRes/Class11_after.java +++ b/plugins/android/testData/addStringRes/Class11_after.java @@ -6,6 +6,6 @@ import p1.p2.R; public class Class extends View { public void f(Context context) { - String s = context.getResources().getString(R.string.hello); + String s = context.getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class12_after.java b/plugins/android/testData/addStringRes/Class12_after.java index 83e438df6bc1..7e60cc7a2b4e 100644 --- a/plugins/android/testData/addStringRes/Class12_after.java +++ b/plugins/android/testData/addStringRes/Class12_after.java @@ -6,6 +6,6 @@ import p1.p2.R; public class Class extends View { public void f() { - String s = getContext().getResources().getString(R.string.hello); + String s = getContext().getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class13.java b/plugins/android/testData/addStringRes/Class13.java index 88386f469397..9e041435687c 100644 --- a/plugins/android/testData/addStringRes/Class13.java +++ b/plugins/android/testData/addStringRes/Class13.java @@ -4,7 +4,7 @@ import android.content.Context; import android.content.res.Resources; public class Class extends Context { - public void f(Resources resources) { + public void f(Context resources) { String s = "hello"; } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class13_after.java b/plugins/android/testData/addStringRes/Class13_after.java index 5ec2a470edd8..09500911aac2 100644 --- a/plugins/android/testData/addStringRes/Class13_after.java +++ b/plugins/android/testData/addStringRes/Class13_after.java @@ -5,7 +5,7 @@ import android.content.res.Resources; import p1.p2.R; public class Class extends Context { - public void f(Resources resources) { - String s = resources.getString(R.string.hello); + public void f(Context resources) { + String s = getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class14.java b/plugins/android/testData/addStringRes/Class14.java index a878406b7b5e..712cfb75b390 100644 --- a/plugins/android/testData/addStringRes/Class14.java +++ b/plugins/android/testData/addStringRes/Class14.java @@ -4,7 +4,7 @@ import android.content.Context; import android.content.res.Resources; public class Class extends Context { - public void f(Resources resources) { + public void f(Context resources) { String s = "hello"; } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class1_after.java b/plugins/android/testData/addStringRes/Class1_after.java index 9ff6724619a7..97da0a51a00a 100644 --- a/plugins/android/testData/addStringRes/Class1_after.java +++ b/plugins/android/testData/addStringRes/Class1_after.java @@ -5,6 +5,6 @@ import p1.p2.R; public class Class { public void f(Context context) { - String s = context.getResources().getString(R.string.hello); + String s = context.getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class2_after.java b/plugins/android/testData/addStringRes/Class2_after.java index 9d15d2e96123..ed96dfac98e1 100644 --- a/plugins/android/testData/addStringRes/Class2_after.java +++ b/plugins/android/testData/addStringRes/Class2_after.java @@ -4,6 +4,6 @@ import p1.p2.R; public class Class { public void f() { - String s = .getResources().getString(R.string.hello); + String s = .getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class3_after.java b/plugins/android/testData/addStringRes/Class3_after.java index 91ccbec6b974..e673d41f2bc3 100644 --- a/plugins/android/testData/addStringRes/Class3_after.java +++ b/plugins/android/testData/addStringRes/Class3_after.java @@ -4,6 +4,6 @@ import android.content.Context; public class Class { public static void f(Context context) { - String s = context.getResources().getString(R.string.hello); + String s = context.getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class4_after.java b/plugins/android/testData/addStringRes/Class4_after.java index 9103cc7e9188..bd29df958460 100644 --- a/plugins/android/testData/addStringRes/Class4_after.java +++ b/plugins/android/testData/addStringRes/Class4_after.java @@ -5,6 +5,6 @@ import p1.p2.R; public class Class extends Context { public void f() { - String s = getResources().getString(R.string.hello); + String s = getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class5_after.java b/plugins/android/testData/addStringRes/Class5_after.java index 1e236c780eb5..da79fab7bb17 100644 --- a/plugins/android/testData/addStringRes/Class5_after.java +++ b/plugins/android/testData/addStringRes/Class5_after.java @@ -5,6 +5,6 @@ import p1.p2.R; public class Class extends Context { public void f(Context context) { - String s = r.getString(R.string.hello); + String s = getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class6_after.java b/plugins/android/testData/addStringRes/Class6_after.java index b7c6406839cb..a93565254d49 100644 --- a/plugins/android/testData/addStringRes/Class6_after.java +++ b/plugins/android/testData/addStringRes/Class6_after.java @@ -4,6 +4,6 @@ import android.content.Context; public class Class extends Context { public static void f(Context context) { - String s = context.getResources().getString(R.string.hello); + String s = context.getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class7_after.java b/plugins/android/testData/addStringRes/Class7_after.java index e86ce36b99d8..00b2c250ff40 100644 --- a/plugins/android/testData/addStringRes/Class7_after.java +++ b/plugins/android/testData/addStringRes/Class7_after.java @@ -5,6 +5,6 @@ import p1.p2.R; public class Class extends Context { public static void f() { - String s = c.getResources().getString(R.string.hello); + String s = c.getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class8.java b/plugins/android/testData/addStringRes/Class8.java index 693f55baf78e..6fc5467ab4e8 100644 --- a/plugins/android/testData/addStringRes/Class8.java +++ b/plugins/android/testData/addStringRes/Class8.java @@ -4,7 +4,7 @@ import android.content.res.Resources; import android.content.Context; public class Class extends Context { - public static void f(Resources resources) { + public static void f(Context resources) { String s = "hello"; } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class8_after.java b/plugins/android/testData/addStringRes/Class8_after.java index 2e14b4212485..f6f6d1ce2364 100644 --- a/plugins/android/testData/addStringRes/Class8_after.java +++ b/plugins/android/testData/addStringRes/Class8_after.java @@ -4,7 +4,7 @@ import android.content.res.Resources; import android.content.Context; public class Class extends Context { - public static void f(Resources resources) { - String s = .getResources().getString(R.string.hello); + public static void f(Context resources) { + String s = resources.getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class9.java b/plugins/android/testData/addStringRes/Class9.java index 1e6f044c4f45..47817692eb10 100644 --- a/plugins/android/testData/addStringRes/Class9.java +++ b/plugins/android/testData/addStringRes/Class9.java @@ -4,7 +4,7 @@ import android.content.res.Resources; import android.content.Context; public class Class extends Context { - public void f(Resources resources) { + public void f(Context context) { String s = "hello"; } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/Class9_after.java b/plugins/android/testData/addStringRes/Class9_after.java index 11dd486a73cd..f5529bd3b025 100644 --- a/plugins/android/testData/addStringRes/Class9_after.java +++ b/plugins/android/testData/addStringRes/Class9_after.java @@ -4,7 +4,7 @@ import android.content.res.Resources; import android.content.Context; public class Class extends Context { - public void f(Resources resources) { - String s = getResources().getString(R.string.hello); + public void f(Context context) { + String s = getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/ClassEscape_after.java b/plugins/android/testData/addStringRes/ClassEscape_after.java index 0658a8a53f82..f92cf49160a9 100644 --- a/plugins/android/testData/addStringRes/ClassEscape_after.java +++ b/plugins/android/testData/addStringRes/ClassEscape_after.java @@ -4,6 +4,6 @@ import android.content.Context; public class ClassEscape { public static void f(Context context) { - String s = context.getResources().getString(R.string.hello); + String s = context.getString(R.string.hello); } } \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/fromLayout.xml b/plugins/android/testData/addStringRes/fromLayout.xml new file mode 100644 index 000000000000..1b496cc10ad8 --- /dev/null +++ b/plugins/android/testData/addStringRes/fromLayout.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/fromLayout1.xml b/plugins/android/testData/addStringRes/fromLayout1.xml new file mode 100644 index 000000000000..f152f913e39d --- /dev/null +++ b/plugins/android/testData/addStringRes/fromLayout1.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/fromLayout_after.xml b/plugins/android/testData/addStringRes/fromLayout_after.xml new file mode 100644 index 000000000000..c074c981ecad --- /dev/null +++ b/plugins/android/testData/addStringRes/fromLayout_after.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/plugins/android/testData/addStringRes/fromManifest.xml b/plugins/android/testData/addStringRes/fromManifest.xml new file mode 100644 index 000000000000..2997ac33cd72 --- /dev/null +++ b/plugins/android/testData/addStringRes/fromManifest.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/plugins/android/testData/addStringRes/fromManifest1.xml b/plugins/android/testData/addStringRes/fromManifest1.xml new file mode 100644 index 000000000000..3b6843287cb9 --- /dev/null +++ b/plugins/android/testData/addStringRes/fromManifest1.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/plugins/android/testData/addStringRes/fromManifest1_after.xml b/plugins/android/testData/addStringRes/fromManifest1_after.xml new file mode 100644 index 000000000000..e7ed000835ae --- /dev/null +++ b/plugins/android/testData/addStringRes/fromManifest1_after.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/plugins/android/testData/addStringRes/fromManifest2.xml b/plugins/android/testData/addStringRes/fromManifest2.xml new file mode 100644 index 000000000000..1f1431a0ecb7 --- /dev/null +++ b/plugins/android/testData/addStringRes/fromManifest2.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/plugins/android/testData/lint/adapterViewChildren.xml b/plugins/android/testData/lint/adapterViewChildren.xml new file mode 100644 index 000000000000..9f92c86e6ecf --- /dev/null +++ b/plugins/android/testData/lint/adapterViewChildren.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/lint/baselineWeights.xml b/plugins/android/testData/lint/baselineWeights.xml new file mode 100644 index 000000000000..a9bb155680e6 --- /dev/null +++ b/plugins/android/testData/lint/baselineWeights.xml @@ -0,0 +1,7 @@ + + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + + diff --git a/plugins/android/testData/lint/baselineWeights_after.xml b/plugins/android/testData/lint/baselineWeights_after.xml new file mode 100644 index 000000000000..65a523c3c9a8 --- /dev/null +++ b/plugins/android/testData/lint/baselineWeights_after.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/plugins/android/testData/lint/contentDescription.xml b/plugins/android/testData/lint/contentDescription.xml new file mode 100644 index 000000000000..f99ee50e6262 --- /dev/null +++ b/plugins/android/testData/lint/contentDescription.xml @@ -0,0 +1,11 @@ + + +geView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + /> + + diff --git a/plugins/android/testData/lint/contentDescription1.xml b/plugins/android/testData/lint/contentDescription1.xml new file mode 100644 index 000000000000..3966e0b9e16a --- /dev/null +++ b/plugins/android/testData/lint/contentDescription1.xml @@ -0,0 +1,11 @@ + + +android:contentDescription=""/> + + diff --git a/plugins/android/testData/lint/contentDescription_after.xml b/plugins/android/testData/lint/contentDescription_after.xml new file mode 100644 index 000000000000..f64caa929953 --- /dev/null +++ b/plugins/android/testData/lint/contentDescription_after.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/plugins/android/testData/lint/convertToDp.xml b/plugins/android/testData/lint/convertToDp.xml new file mode 100644 index 000000000000..6bc4c0dc8d8e --- /dev/null +++ b/plugins/android/testData/lint/convertToDp.xml @@ -0,0 +1,6 @@ + + + android:width="100px"/> + \ No newline at end of file diff --git a/plugins/android/testData/lint/convertToDp_after.xml b/plugins/android/testData/lint/convertToDp_after.xml new file mode 100644 index 000000000000..17c930637291 --- /dev/null +++ b/plugins/android/testData/lint/convertToDp_after.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/plugins/android/testData/lint/duplicatedIds.xml b/plugins/android/testData/lint/duplicatedIds.xml new file mode 100644 index 000000000000..3f200a1b60bf --- /dev/null +++ b/plugins/android/testData/lint/duplicatedIds.xml @@ -0,0 +1,17 @@ + + + + android:id="@+id/id123" + /> + + diff --git a/plugins/android/testData/lint/editText.xml b/plugins/android/testData/lint/editText.xml new file mode 100644 index 000000000000..afd1ab54fc0d --- /dev/null +++ b/plugins/android/testData/lint/editText.xml @@ -0,0 +1,6 @@ + + + ayout_height="wrap_content"/> + \ No newline at end of file diff --git a/plugins/android/testData/lint/editText_after.xml b/plugins/android/testData/lint/editText_after.xml new file mode 100644 index 000000000000..f2c4ad49423f --- /dev/null +++ b/plugins/android/testData/lint/editText_after.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/plugins/android/testData/lint/exportedService.xml b/plugins/android/testData/lint/exportedService.xml new file mode 100644 index 000000000000..b62fd1fc5b0a --- /dev/null +++ b/plugins/android/testData/lint/exportedService.xml @@ -0,0 +1,9 @@ + + + + android:exported="true"/> + + diff --git a/plugins/android/testData/lint/exportedService_after.xml b/plugins/android/testData/lint/exportedService_after.xml new file mode 100644 index 000000000000..014758a117f9 --- /dev/null +++ b/plugins/android/testData/lint/exportedService_after.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/plugins/android/testData/lint/hardcodedQuickfix.xml b/plugins/android/testData/lint/hardcodedQuickfix.xml new file mode 100644 index 000000000000..85cc775f1f5a --- /dev/null +++ b/plugins/android/testData/lint/hardcodedQuickfix.xml @@ -0,0 +1,12 @@ + + +android:text="Hello World, MyActivity" + /> + + diff --git a/plugins/android/testData/lint/hardcodedQuickfix1.xml b/plugins/android/testData/lint/hardcodedQuickfix1.xml new file mode 100644 index 000000000000..dc34d4a6e764 --- /dev/null +++ b/plugins/android/testData/lint/hardcodedQuickfix1.xml @@ -0,0 +1,12 @@ + + +android:text="Hello World, MyActivity" + /> + + diff --git a/plugins/android/testData/lint/hardcodedQuickfix1_after.xml b/plugins/android/testData/lint/hardcodedQuickfix1_after.xml new file mode 100644 index 000000000000..97da65a0b131 --- /dev/null +++ b/plugins/android/testData/lint/hardcodedQuickfix1_after.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/plugins/android/testData/lint/hardcodedQuickfix_after.xml b/plugins/android/testData/lint/hardcodedQuickfix_after.xml new file mode 100644 index 000000000000..97da65a0b131 --- /dev/null +++ b/plugins/android/testData/lint/hardcodedQuickfix_after.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/plugins/android/testData/lint/inefficientWeight.xml b/plugins/android/testData/lint/inefficientWeight.xml new file mode 100644 index 000000000000..65ebfc8958cf --- /dev/null +++ b/plugins/android/testData/lint/inefficientWeight.xml @@ -0,0 +1,12 @@ + + + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="10" + /> + + diff --git a/plugins/android/testData/lint/inefficientWeight_after.xml b/plugins/android/testData/lint/inefficientWeight_after.xml new file mode 100644 index 000000000000..ae8f06788487 --- /dev/null +++ b/plugins/android/testData/lint/inefficientWeight_after.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/plugins/android/testData/lint/missingPrefix.xml b/plugins/android/testData/lint/missingPrefix.xml new file mode 100644 index 000000000000..6934bcbf6f10 --- /dev/null +++ b/plugins/android/testData/lint/missingPrefix.xml @@ -0,0 +1,12 @@ + + + id="@+id/id123" + /> + + diff --git a/plugins/android/testData/lint/missingPrefix1.xml b/plugins/android/testData/lint/missingPrefix1.xml new file mode 100644 index 000000000000..3db882a6da7b --- /dev/null +++ b/plugins/android/testData/lint/missingPrefix1.xml @@ -0,0 +1,12 @@ + + + id="@+id/id123" + /> + + diff --git a/plugins/android/testData/lint/missingPrefix1_after.xml b/plugins/android/testData/lint/missingPrefix1_after.xml new file mode 100644 index 000000000000..6c736a2de094 --- /dev/null +++ b/plugins/android/testData/lint/missingPrefix1_after.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/plugins/android/testData/lint/missingPrefix_after.xml b/plugins/android/testData/lint/missingPrefix_after.xml new file mode 100644 index 000000000000..e007df753563 --- /dev/null +++ b/plugins/android/testData/lint/missingPrefix_after.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/plugins/android/testData/lint/obsoleteLayoutParams.xml b/plugins/android/testData/lint/obsoleteLayoutParams.xml new file mode 100644 index 000000000000..4c57fe6e7006 --- /dev/null +++ b/plugins/android/testData/lint/obsoleteLayoutParams.xml @@ -0,0 +1,6 @@ + + + android:layout_row="10"/> + \ No newline at end of file diff --git a/plugins/android/testData/lint/obsoleteLayoutParams_after.xml b/plugins/android/testData/lint/obsoleteLayoutParams_after.xml new file mode 100644 index 000000000000..1f0a1aed7c07 --- /dev/null +++ b/plugins/android/testData/lint/obsoleteLayoutParams_after.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/plugins/android/testData/lint/scrollViewChildren.xml b/plugins/android/testData/lint/scrollViewChildren.xml new file mode 100644 index 000000000000..55660b7e40b5 --- /dev/null +++ b/plugins/android/testData/lint/scrollViewChildren.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/lint/scrollViewSize.xml b/plugins/android/testData/lint/scrollViewSize.xml new file mode 100644 index 000000000000..a5ee7b2c3647 --- /dev/null +++ b/plugins/android/testData/lint/scrollViewSize.xml @@ -0,0 +1,8 @@ + + + + android:layout_height="fill_parent"/> + + \ No newline at end of file diff --git a/plugins/android/testData/lint/scrollViewSize_after.xml b/plugins/android/testData/lint/scrollViewSize_after.xml new file mode 100644 index 000000000000..eb95fc26ed31 --- /dev/null +++ b/plugins/android/testData/lint/scrollViewSize_after.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/plugins/android/testData/lint/typographyDashes.xml b/plugins/android/testData/lint/typographyDashes.xml new file mode 100644 index 000000000000..7b68d3f92e0e --- /dev/null +++ b/plugins/android/testData/lint/typographyDashes.xml @@ -0,0 +1,4 @@ + + + 10-20 + \ No newline at end of file diff --git a/plugins/android/testData/lint/typographyDashes_after.xml b/plugins/android/testData/lint/typographyDashes_after.xml new file mode 100644 index 000000000000..35404e3362b2 --- /dev/null +++ b/plugins/android/testData/lint/typographyDashes_after.xml @@ -0,0 +1,4 @@ + + + 10–20 + \ No newline at end of file diff --git a/plugins/android/testData/lint/typographyQuotes.xml b/plugins/android/testData/lint/typographyQuotes.xml new file mode 100644 index 000000000000..cfe2294a0685 --- /dev/null +++ b/plugins/android/testData/lint/typographyQuotes.xml @@ -0,0 +1,5 @@ + + + untitled303 + aba 'aba' + diff --git a/plugins/android/testData/lint/typographyQuotes_after.xml b/plugins/android/testData/lint/typographyQuotes_after.xml new file mode 100644 index 000000000000..f0539ff7ece6 --- /dev/null +++ b/plugins/android/testData/lint/typographyQuotes_after.xml @@ -0,0 +1,5 @@ + + + untitled303 + aba ‘aba’ + diff --git a/plugins/android/testData/lint/uselessLeaf.xml b/plugins/android/testData/lint/uselessLeaf.xml new file mode 100644 index 000000000000..ae483929b35d --- /dev/null +++ b/plugins/android/testData/lint/uselessLeaf.xml @@ -0,0 +1,6 @@ + + + h="wrap_content" android:layout_height="wrap_content"/> + \ No newline at end of file diff --git a/plugins/android/testData/lint/uselessLeaf_after.xml b/plugins/android/testData/lint/uselessLeaf_after.xml new file mode 100644 index 000000000000..68d85c5e154b --- /dev/null +++ b/plugins/android/testData/lint/uselessLeaf_after.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/plugins/android/testData/lint/uselessParent.xml b/plugins/android/testData/lint/uselessParent.xml new file mode 100644 index 000000000000..54a49cc510f5 --- /dev/null +++ b/plugins/android/testData/lint/uselessParent.xml @@ -0,0 +1,8 @@ + + + r android:layout_width="wrap_content" android:layout_height="wrap_content"> + + + \ No newline at end of file diff --git a/plugins/android/testSrc/org/jetbrains/android/AndroidLintTest.java b/plugins/android/testSrc/org/jetbrains/android/AndroidLintTest.java new file mode 100644 index 000000000000..dfcd39e34d20 --- /dev/null +++ b/plugins/android/testSrc/org/jetbrains/android/AndroidLintTest.java @@ -0,0 +1,217 @@ +package org.jetbrains.android; + +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.android.inspections.lint.*; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidLintTest extends AndroidTestCase { + @NonNls private static final String BASE_PATH = "/lint/"; + + public AndroidLintTest() { + super(false); + } + + public void testHardcodedQuickfix() throws Exception { + doTestHardcodedQuickfix(); + } + + public void testHardcodedQuickfix1() throws Exception { + doTestHardcodedQuickfix(); + } + + private void doTestHardcodedQuickfix() throws IOException { + final IntentionAction action = doTestHighlightingAndGetQuickfix( + new AndroidLintInspectionToolProvider.AndroidLintHardcodedTextInspection(), + AndroidBundle.message("add.string.resource.intention.text"), false ? "AndroidManifest.xml" : "/res/layout/layout.xml", "xml"); + assertNotNull(action); + assertTrue(action.isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile())); + + new WriteCommandAction(myFixture.getProject(), "") { + @Override + protected void run(Result result) throws Throwable { + ((AndroidAddStringResourceQuickFix)action) + .invokeIntention(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), "hello"); + } + }.execute(); + + myFixture.checkResultByFile(BASE_PATH + getTestName(true) + "_after.xml"); + } + + public void testContentDescription() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintContentDescriptionInspection(), + AndroidBundle.message("android.lint.inspections.add.content.description"), + "/res/layout/layout.xml", "xml"); + } + + public void testContentDescription1() throws Exception { + doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintContentDescriptionInspection(), + "/res/layout/layout.xml", "xml"); + } + + public void testAdapterViewChildren() throws Exception { + doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintAdapterViewChildrenInspection(), + "/res/layout/layout.xml", "xml"); + } + + public void testScrollViewChildren() throws Exception { + doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintScrollViewCountInspection(), + "/res/layout/layout.xml", "xml"); + } + + public void testMissingPrefix() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintMissingPrefixInspection(), + AndroidBundle.message("android.lint.inspections.add.android.prefix"), + "/res/layout/layout.xml", "xml"); + } + + public void testMissingPrefix1() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintMissingPrefixInspection(), + AndroidBundle.message("android.lint.inspections.add.android.prefix"), + "/res/layout/layout.xml", "xml"); + } + + public void testDuplicatedIds() throws Exception { + doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintDuplicateIdsInspection(), + "/res/layout/layout.xml", "xml"); + } + + public void testInefficientWeight() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintInefficientWeightInspection(), + AndroidBundle.message("android.lint.inspections.replace.with.zero.dp"), + "/res/layout/layout.xml", "xml"); + } + + public void testBaselineWeights() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintDisableBaselineAlignmentInspection(), + AndroidBundle.message("android.lint.inspections.set.baseline.attribute"), + "/res/layout/layout.xml", "xml"); + } + + public void testObsoleteLayoutParams() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintObsoleteLayoutParamInspection(), + AndroidBundle.message("android.lint.inspections.remove.attribute"), + "/res/layout/layout.xml", "xml"); + } + + public void testConvertToDp() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintPxUsageInspection(), + AndroidBundle.message("android.lint.inspections.convert.to.dp"), + "/res/layout/layout.xml", "xml"); + } + + public void testScrollViewSize() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintScrollViewSizeInspection(), + AndroidBundle.message("android.lint.inspections.set.to.wrap.content"), + "/res/layout/layout.xml", "xml"); + } + + public void testExportedService() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintExportedServiceInspection(), + AndroidBundle.message("android.lint.inspections.add.permission.attribute"), + "AndroidManifest.xml", "xml"); + } + + public void testEditText() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintTextFieldsInspection(), + AndroidBundle.message("android.lint.inspections.add.input.type.attribute"), + "/res/layout/layout.xml", "xml"); + } + + public void testUselessLeaf() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintUselessLeafInspection(), + AndroidBundle.message("android.lint.inspections.remove.unnecessary.view"), + "/res/layout/layout.xml", "xml"); + } + + public void testUselessParent() throws Exception { + doTestNoFix(new AndroidLintInspectionToolProvider.AndroidLintUselessParentInspection(), + "/res/layout/layout.xml", "xml"); + } + + /*public void testTypographyDashes() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintTypographyDashesInspection(), + AndroidBundle.message("android.lint.inspections.replace.with.suggested.characters"), + "/res/values/typography.xml", "xml"); + } + + public void testTypographyQuotes() throws Exception { + doTestWithFix(new AndroidLintInspectionToolProvider.AndroidLintTypographyQuotesInspection(), + AndroidBundle.message("android.lint.inspections.replace.with.suggested.characters"), + "/res/values/typography.xml", "xml"); + }*/ + + private void doTestNoFix(@NotNull AndroidLintInspectionBase inspection, @NotNull String copyTo, @NotNull String extension) + throws IOException { + doTestHighlighting(inspection, copyTo, extension); + + IntentionAction action = null; + + for (IntentionAction a : myFixture.getAvailableIntentions()) { + if (a instanceof AndroidLintExternalAnnotator.MyFixingIntention) { + action = a; + } + } + assertNull(action); + } + + private void doTestWithFix(@NotNull AndroidLintInspectionBase inspection, + @NotNull String message, + @NotNull String copyTo, + @NotNull String extension) + throws IOException { + final IntentionAction action = doTestHighlightingAndGetQuickfix(inspection, message, copyTo, extension); + assertNotNull(action); + assertTrue(action.isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile())); + + new WriteCommandAction(myFixture.getProject(), "") { + @Override + protected void run(Result result) throws Throwable { + action.invoke(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile()); + } + }.execute(); + + myFixture.checkResultByFile(BASE_PATH + getTestName(true) + "_after." + extension); + } + + @Nullable + private IntentionAction doTestHighlightingAndGetQuickfix(@NotNull AndroidLintInspectionBase inspection, + @NotNull String message, + @NotNull String copyTo, + @NotNull String extension) throws IOException { + doTestHighlighting(inspection, copyTo, extension); + + IntentionAction action = null; + + for (IntentionAction a : myFixture.getAvailableIntentions()) { + if (message.equals(a.getText())) { + action = a; + } + } + return action; + } + + private void doTestHighlighting(@NotNull AndroidLintInspectionBase inspection, @NotNull String copyTo, @NotNull String extension) + throws IOException { + if (!"AndroidManifest.xml".equals(copyTo)) { + createManifest(); + } + + myFixture.enableInspections(new GlobalInspectionToolWrapper(inspection)); + final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + "." + extension, copyTo); + myFixture.configureFromExistingVirtualFile(file); + myFixture.doHighlighting(); + myFixture.checkHighlighting(true, false, false); + } +} diff --git a/plugins/android/testSrc/org/jetbrains/android/intentions/AndroidAddStringResourceActionTest.java b/plugins/android/testSrc/org/jetbrains/android/intentions/AndroidAddStringResourceActionTest.java index b58ff1cd2633..c2970ace69c0 100644 --- a/plugins/android/testSrc/org/jetbrains/android/intentions/AndroidAddStringResourceActionTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/intentions/AndroidAddStringResourceActionTest.java @@ -11,12 +11,18 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.android.AndroidTestCase; import org.jetbrains.annotations.Nullable; +import java.io.IOException; + /** * @author Eugene.Kudelevsky */ public class AndroidAddStringResourceActionTest extends AndroidTestCase { private static final String BASE_PATH = "addStringRes/"; + public AndroidAddStringResourceActionTest() { + super(false); + } + @Override public void setUp() throws Exception { super.setUp(); @@ -29,37 +35,31 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase { super.tearDown(); } - public void test1() { + public void test1() throws IOException { doTest(); } - public void test2() { + public void test2() throws IOException { doTest(); } - public void test3() { + public void test3() throws IOException { doTest(); } - public void test4() { + public void test4() throws IOException { doTest(); } - public void test5() { - doTest(new Runnable() { - @Override - public void run() { - myFixture.type('r'); - TemplateManagerImpl.getTemplateState(myFixture.getEditor()).nextTab(); - } - }); - } - - public void test6() { + public void test5() throws IOException { doTest(); } - public void test7() { + public void test6() throws IOException { + doTest(); + } + + public void test7() throws IOException { doTest(new Runnable() { @Override public void run() { @@ -69,24 +69,19 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase { }); } - public void test8() { - doTest(new Runnable() { - @Override - public void run() { - myFixture.performEditorAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM); - } - }); - } - - public void test9() { + public void test8() throws IOException { doTest(); } - public void test10() { + public void test9() throws IOException { doTest(); } - public void test11() { + public void test10() throws IOException { + doTest(); + } + + public void test11() throws IOException { doTest(new Runnable() { @Override public void run() { @@ -96,18 +91,12 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase { }); } - public void test12() { + public void test12() throws IOException { doTest(); } - public void test13() { - doTest(new Runnable() { - @Override - public void run() { - myFixture.type("r"); - myFixture.performEditorAction(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM); - } - }); + public void test13() throws IOException { + doTest(); } public void test14() { @@ -117,36 +106,68 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase { assertFalse(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), javaPsiFile)); } - public void testEscape() { + public void testEscape() throws IOException { doTest(getTestName(false), "strings.xml", null, true, "strings_escape_after.xml"); } - public void testNewFile() { + public void testNewFile() throws IOException { doTest("1", null, null, true); } - public void testInvalidStringsXml() { + public void testInvalidStringsXml() throws IOException { try { doTest("1", "strings_invalid.xml", null, true); fail(); } catch (IncorrectOperationException e) { // in normal mode error dialog will be shown - assertEquals("invalid strings.xml", e.getMessage()); + assertEquals("File strings.xml is not resource file", e.getMessage()); } myFixture.checkResultByFile(BASE_PATH + "Class1.java"); myFixture.checkResultByFile("res/values/strings.xml", BASE_PATH + "strings_invalid.xml", false); } - private void doTest() { + public void testFromLayout() throws IOException { + createManifest(); + final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "res/layout/layout.xml"); + doExtractAndCheckStringsXml("strings.xml", null, true, "strings_after.xml", file); + myFixture.checkResultByFile(BASE_PATH + getTestName(true) + "_after.xml"); + } + + public void testFromLayout1() throws IOException { + createManifest(); + final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "res/layout/layout.xml"); + myFixture.configureFromExistingVirtualFile(file); + assertFalse(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile())); + } + + public void testFromManifest() { + final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "AndroidManifest.xml"); + myFixture.configureFromExistingVirtualFile(file); + assertFalse(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile())); + } + + public void testFromManifest1() { + final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "AndroidManifest.xml"); + doExtractAndCheckStringsXml("strings.xml", null, true, "strings_after.xml", file); + myFixture.checkResultByFile(BASE_PATH + getTestName(true) + "_after.xml"); + } + + public void testFromManifest2() { + final VirtualFile file = myFixture.copyFileToProject(BASE_PATH + getTestName(true) + ".xml", "AndroidManifest.xml"); + myFixture.configureFromExistingVirtualFile(file); + assertFalse(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile())); + } + + private void doTest() throws IOException { doTest(getTestName(false), "strings.xml", null, true); } - private void doTest(Runnable invokeAfterTemplate) { + private void doTest(Runnable invokeAfterTemplate) throws IOException { doTest(getTestName(false), "strings.xml", invokeAfterTemplate, false); } - private void doTest(String testName, String stringsXml, final Runnable invokeAfterTemplate, final boolean closePopup) { + private void doTest(String testName, String stringsXml, final Runnable invokeAfterTemplate, final boolean closePopup) throws IOException { doTest(testName, stringsXml, invokeAfterTemplate, closePopup, "strings_after.xml"); } @@ -154,12 +175,22 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase { String stringsXml, @Nullable final Runnable invokeAfterTemplate, final boolean closePopup, - String stringsAfter) { + String stringsAfter) throws IOException { + createManifest(); + VirtualFile javaFile = myFixture.copyFileToProject(BASE_PATH + "Class" + testName + ".java", "src/p1/p2/Class.java"); + doExtractAndCheckStringsXml(stringsXml, invokeAfterTemplate, closePopup, stringsAfter, javaFile); + myFixture.checkResultByFile(BASE_PATH + "Class" + testName + "_after.java"); + } + + private void doExtractAndCheckStringsXml(String stringsXml, + final Runnable invokeAfterTemplate, + final boolean closePopup, + String stringsAfter, + VirtualFile javaFile) { if (stringsXml != null) { myFixture.copyFileToProject(BASE_PATH + stringsXml, "res/values/strings.xml"); } myFixture.copyFileToProject(BASE_PATH + "R.java", "src/p1/p2/R.java"); - VirtualFile javaFile = myFixture.copyFileToProject(BASE_PATH + "Class" + testName + ".java", "src/p1/p2/Class.java"); myFixture.configureFromExistingVirtualFile(javaFile); final PsiFile javaPsiFile = myFixture.getFile(); assertTrue(new AndroidAddStringResourceAction().isAvailable(myFixture.getProject(), myFixture.getEditor(), javaPsiFile)); @@ -180,7 +211,6 @@ public class AndroidAddStringResourceActionTest extends AndroidTestCase { } } }, "", ""); - myFixture.checkResultByFile(BASE_PATH + "Class" + testName + "_after.java"); myFixture.checkResultByFile("res/values/strings.xml", BASE_PATH + stringsAfter, false); } } diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index 330e83d18fec..7a2ea5d49803 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -115,8 +115,8 @@
- + diff --git a/plugins/devkit/src/actions/GenerateHighlightingMarkupAction.java b/plugins/devkit/src/actions/ToggleHighlightingMarkupAction.java similarity index 53% rename from plugins/devkit/src/actions/GenerateHighlightingMarkupAction.java rename to plugins/devkit/src/actions/ToggleHighlightingMarkupAction.java index 540e7ef751ad..33fb334b63ff 100644 --- a/plugins/devkit/src/actions/GenerateHighlightingMarkupAction.java +++ b/plugins/devkit/src/actions/ToggleHighlightingMarkupAction.java @@ -28,6 +28,7 @@ import com.intellij.openapi.command.CommandProcessorEx; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import com.intellij.util.Processor; @@ -35,11 +36,13 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.LinkedList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @author gregsh */ -public class GenerateHighlightingMarkupAction extends AnAction { +public class ToggleHighlightingMarkupAction extends AnAction { @Override public void update(AnActionEvent e) { Editor editor = PlatformDataKeys.EDITOR.getData(e.getDataContext()); @@ -53,60 +56,98 @@ public class GenerateHighlightingMarkupAction extends AnAction { PsiFile file = LangDataKeys.PSI_FILE.getData(e.getDataContext()); if (editor == null || file == null) return; final Project project = file.getProject(); - CommandProcessorEx commandProcessorEx = (CommandProcessorEx)CommandProcessorEx.getInstance(); - Object commandToken = commandProcessorEx - .startCommand(project, e.getPresentation().getText(), e.getPresentation().getText(), UndoConfirmationPolicy.DEFAULT); + CommandProcessorEx commandProcessor = (CommandProcessorEx)CommandProcessorEx.getInstance(); + Object commandToken = commandProcessor.startCommand(project, e.getPresentation().getText(), e.getPresentation().getText(), UndoConfirmationPolicy.DEFAULT); AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(getClass()); try { - perform(project, editor.getDocument()); + final SelectionModel selectionModel = editor.getSelectionModel(); + int[] starts = selectionModel.getBlockSelectionStarts(); + int[] ends = selectionModel.getBlockSelectionEnds(); + + int startOffset = starts.length == 0? 0 : starts[0]; + int endOffset = ends.length == 0? editor.getDocument().getTextLength() : ends[ends.length - 1]; + + perform(project, editor.getDocument(), startOffset, endOffset); } finally { token.finish(); - commandProcessorEx.finishCommand(project, commandToken, null); + commandProcessor.finishCommand(project, commandToken, null); } } - private static void perform(Project project, final Document document) { + private static void perform(Project project, final Document document, int startOffset, final int endOffset) { final CharSequence sequence = document.getCharsSequence(); final StringBuilder sb = new StringBuilder(); - final int[] offset = new int[] {0}; - final ArrayList infos = new ArrayList(); - DaemonCodeAnalyzerImpl.processHighlights( - document, project, HighlightSeverity.WARNING, 0, sequence.length(), - new Processor() { - @Override - public boolean process(HighlightInfo info) { - if (info.severity != HighlightSeverity.WARNING && info.severity != HighlightSeverity.ERROR) return true; - offset[0] = appendInfo(info, sb, sequence, offset[0], infos); - return true; + Pattern pattern = Pattern.compile("<(error|warning|EOLError|EOLWarning|info|weak_warning)((?:\\s|=|\\w+|\\\"(?:[^\"]|\\\\\\\")*?\\\")*?)>(.*?)"); + Matcher matcher = pattern.matcher(sequence); + sb.append(sequence, 0, startOffset); + if (matcher.find(startOffset)) { + boolean compactMode = false; + int pos = startOffset; + do { + if (matcher.start(0) >= endOffset) break; + if (matcher.start(2) < matcher.end(2)) { + if (!compactMode) { + sb.setLength(pos = 0); + compactMode = true; + } + sb.append(sequence, pos, matcher.start(2)); + sb.append(sequence, matcher.end(2), matcher.end(0)); } - }); - offset[0] = appendInfo(null, sb, sequence, offset[0], infos); - sb.append(sequence.subSequence(offset[0], sequence.length())); + else if (!compactMode) { + sb.append(sequence, pos, matcher.start(0)); + sb.append(sequence, matcher.start(3), matcher.end(3)); + } + pos = matcher.end(0); + } + while (matcher.find(pos)); + sb.append(sequence, pos, sequence.length()); + } + else { + final int[] offset = new int[] {startOffset}; + final ArrayList infos = new ArrayList(); + DaemonCodeAnalyzerImpl.processHighlights( + document, project, HighlightSeverity.WARNING, 0, sequence.length(), + new Processor() { + @Override + public boolean process(HighlightInfo info) { + if (info.severity != HighlightSeverity.WARNING && info.severity != HighlightSeverity.ERROR) return true; + if (info.getStartOffset() >= endOffset) return false; + offset[0] = appendInfo(info, sb, sequence, offset[0], infos, false); + return true; + } + }); + offset[0] = appendInfo(null, sb, sequence, offset[0], infos, false); + sb.append(sequence.subSequence(offset[0], sequence.length())); + } document.setText(sb); } - private static int appendInfo(@Nullable HighlightInfo info, StringBuilder sb, CharSequence sequence, int offset, ArrayList infos) { + private static int appendInfo(@Nullable HighlightInfo info, + StringBuilder sb, + CharSequence sequence, + int offset, + ArrayList infos, final boolean compact) { if (info == null || !infos.isEmpty() && infos.get(infos.size() - 1).getEndOffset() < info.getStartOffset()) { if (infos.size() == 1) { HighlightInfo cur = infos.remove(0); sb.append(sequence.subSequence(offset, cur.getStartOffset())); - sb.append(cur.severity == HighlightSeverity.WARNING ? "" : ""); + appendTag(sb, cur, true, compact); sb.append(sequence.subSequence(cur.getStartOffset(), cur.getEndOffset())); - sb.append(cur.severity == HighlightSeverity.WARNING ? "" : ""); + appendTag(sb, cur, false, compact); offset = cur.getEndOffset(); } else { // process overlapped LinkedList stack = new LinkedList(); for (HighlightInfo cur : infos) { - offset = processStack(stack, sb, sequence, offset, cur.getStartOffset()); + offset = processStack(stack, sb, sequence, offset, cur.getStartOffset(), compact); sb.append(sequence.subSequence(offset, cur.getStartOffset())); offset = cur.getStartOffset(); - sb.append(cur.severity == HighlightSeverity.WARNING ? "" : ""); + appendTag(sb, cur, true, compact); stack.addLast(cur); } - offset = processStack(stack, sb, sequence, offset, sequence.length()); + offset = processStack(stack, sb, sequence, offset, sequence.length(), compact); infos.clear(); } } @@ -127,7 +168,8 @@ public class GenerateHighlightingMarkupAction extends AnAction { StringBuilder sb, CharSequence sequence, int offset, - final int endOffset) { + final int endOffset, + final boolean compact) { if (stack.isEmpty()) return offset; for (HighlightInfo cur = stack.peekLast(); cur != null && cur.getEndOffset() <= endOffset; cur = stack.peekLast()) { stack.removeLast(); @@ -138,8 +180,24 @@ public class GenerateHighlightingMarkupAction extends AnAction { //System.out.println("Incorrect overlapping infos: " + offset + " > " + cur.getEndOffset()); } offset = cur.getEndOffset(); - sb.append(cur.severity == HighlightSeverity.WARNING ? "" : ""); + appendTag(sb, cur, false, compact); } return offset; } + + private static void appendTag(StringBuilder sb, HighlightInfo cur, boolean opening, final boolean compact) { + sb.append("<"); + if (!opening) sb.append("/"); + if (cur.isAfterEndOfLine) { + sb.append(cur.severity == HighlightSeverity.WARNING ? "EOLWarning" : "EOLError"); + } + else { + sb.append(cur.severity == HighlightSeverity.WARNING ? "warning" : "error"); + } + if (opening && !compact) { + sb.append(" descr=\"").append(cur.description).append("\""); + + } + sb.append(">"); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java index f4e2b1452ea0..c93b5fe07aaf 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java @@ -33,10 +33,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Trinity; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileAdapter; -import com.intellij.openapi.vfs.VirtualFileEvent; -import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; @@ -196,6 +193,18 @@ public class MvcModuleStructureSynchronizer extends AbstractProjectComponent { queue(SyncAction.UpdateProjectStructure, event.getFile()); } } + + @Override + public void fileMoved(VirtualFileMoveEvent event) { + myModificationCount++; + } + + @Override + public void propertyChanged(VirtualFilePropertyEvent event) { + if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) { + myModificationCount++; + } + } })); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/projectView/MvcProjectViewPane.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/projectView/MvcProjectViewPane.java index b8e88faf0ca2..67eb6591b9b0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/projectView/MvcProjectViewPane.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/projectView/MvcProjectViewPane.java @@ -21,9 +21,11 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.*; +import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.psi.*; import com.intellij.ui.AutoScrollFromSourceHandler; import com.intellij.ui.AutoScrollToSourceHandler; +import com.intellij.ui.content.ContentManager; import com.intellij.util.Alarm; import com.intellij.util.IJSwingUtilities; import com.intellij.util.containers.ContainerUtil; @@ -67,7 +69,7 @@ import java.util.List; */ public class MvcProjectViewPane extends AbstractProjectViewPSIPane implements IdeView { private final CopyPasteDelegator myCopyPasteDelegator; - private final JPanel myComponent; + private final JComponent myComponent; private final DeleteProvider myDeletePSIElementProvider; private final ModuleDeleteProvider myDeleteModuleProvider = new ModuleDeleteProvider(); @@ -120,9 +122,7 @@ public class MvcProjectViewPane extends AbstractProjectViewPSIPane implements Id project.getMessageBus().connect(this).subscribe(PsiModificationTracker.TOPIC, new TreeUpdater()); - myComponent = new JPanel(new BorderLayout()); - myComponent.add(createComponent(), BorderLayout.CENTER); - myComponent.add(createToolbar(), BorderLayout.NORTH); + myComponent = createComponent(); DataManager.registerDataProvider(myComponent, this); myCopyPasteDelegator = new CopyPasteDelegator(project, myComponent) { @@ -135,18 +135,33 @@ public class MvcProjectViewPane extends AbstractProjectViewPSIPane implements Id myDeletePSIElementProvider = new DeleteHandler.DefaultDeleteProvider(); } - private JComponent createToolbar() { + public void setup(ToolWindowEx toolWindow) { + JPanel p = new JPanel(new BorderLayout()); + p.add(myComponent, BorderLayout.CENTER); + + ContentManager contentManager = toolWindow.getContentManager(); + Content content = contentManager.getFactory().createContent(p, null, false); + content.setDisposer(this); + content.setCloseable(false); + + content.setPreferredFocusableComponent(createComponent()); + contentManager.addContent(content); + + contentManager.setSelectedContent(content, true); + DefaultActionGroup group = new DefaultActionGroup(); + group.add(new HideEmptyMiddlePackagesAction()); + group.add(myAutoScrollToSourceHandler.createToggleAction()); + group.add(myAutoScrollFromSourceHandler.createToggleAction()); - final TreeExpander expander = new DefaultTreeExpander(myTree); - final CommonActionsManager actionsManager = CommonActionsManager.getInstance(); - group.addAction(new ScrollFromSourceAction()); - group.addAction(myAutoScrollFromSourceHandler.createToggleAction()).setAsSecondary(true); - group.addAction(myAutoScrollToSourceHandler.createToggleAction()).setAsSecondary(true); - group.add(actionsManager.createCollapseAllAction(expander, myTree)); - group.addAction(new HideEmptyMiddlePackagesAction()).setAsSecondary(true); + toolWindow.setAdditionalGearActions(group); - return ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent(); + TreeExpander expander = new DefaultTreeExpander(myTree); + CommonActionsManager actionsManager = CommonActionsManager.getInstance(); + AnAction collapseAction = actionsManager.createCollapseAllAction(expander, myTree); + collapseAction.getTemplatePresentation().setIcon(IconLoader.getIcon("/general/collapseAll.png")); + + toolWindow.setTitleActions(new AnAction[]{new ScrollFromSourceAction(), collapseAction}); } @Override @@ -158,10 +173,6 @@ public class MvcProjectViewPane extends AbstractProjectViewPSIPane implements Id return component; } - public JPanel getComponent() { - return myComponent; - } - public String getTitle() { throw new UnsupportedOperationException(); } @@ -436,10 +447,9 @@ public class MvcProjectViewPane extends AbstractProjectViewPSIPane implements Id scrollFromSource(); } - private class ScrollFromSourceAction extends AnAction implements DumbAware { private ScrollFromSourceAction() { - super("Scroll from Source", "Select the file open in the active editor", IconLoader.getIcon("/general/autoscrollFromSource.png")); + super("Scroll from Source", "Select the file open in the active editor", IconLoader.getIcon("/general/locate.png")); } @Override @@ -475,7 +485,7 @@ public class MvcProjectViewPane extends AbstractProjectViewPSIPane implements Id myAlarm.cancelAllRequests(); myAlarm.addRequest(new Runnable() { public void run() { - if (myProject.isDisposed() || !getComponent().isShowing()) return; + if (myProject.isDisposed() || !myComponent.isShowing()) return; if (myAutoScrollFromSource) { selectElementAtCaretNotLosingFocus(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/projectView/MvcToolWindowDescriptor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/projectView/MvcToolWindowDescriptor.java index 265df37d0a87..9f9e4d8bbd52 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/projectView/MvcToolWindowDescriptor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/projectView/MvcToolWindowDescriptor.java @@ -25,17 +25,15 @@ import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; -import com.intellij.ui.content.Content; -import com.intellij.ui.content.ContentManager; +import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.mvc.MvcFramework; import org.jetbrains.plugins.groovy.mvc.MvcModuleStructureUtil; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.awt.*; import java.util.List; /** @@ -58,20 +56,8 @@ public abstract class MvcToolWindowDescriptor implements ToolWindowFactory, Cond toolWindow.setToHideOnEmptyContent(true); toolWindow.setTitle(myFramework.getDisplayName()); - final MvcProjectViewPane view = new MvcProjectViewPane(project, this); - - final JPanel p = new JPanel(new BorderLayout()); - p.add(view.getComponent(), BorderLayout.CENTER); - - final ContentManager contentManager = toolWindow.getContentManager(); - final Content content = contentManager.getFactory().createContent(p, null, false); - content.setDisposer(view); - content.setCloseable(false); - - content.setPreferredFocusableComponent(view.createComponent()); - contentManager.addContent(content); - - contentManager.setSelectedContent(content, true); + MvcProjectViewPane view = new MvcProjectViewPane(project, this); + view.setup((ToolWindowEx)toolWindow); } public boolean value(Project project) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy index 17df5b873993..4aab9d2fc036 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTest.groovy @@ -65,6 +65,14 @@ public abstract class GroovyCompilerTest extends GroovyCompilerTestCase { assertOutput("Foo", "239"); setFileText(file, "class Bar {}"); + makeShouldFail() + + setFileText(file, barText); + make(); + assertOutput("Foo", "239"); + } + + private void makeShouldFail() { try { make(); fail("Make should fail"); @@ -74,10 +82,6 @@ public abstract class GroovyCompilerTest extends GroovyCompilerTestCase { throw e; } } - - setFileText(file, barText); - make(); - assertOutput("Foo", "239"); } public void testRenameToJava() throws Throwable { @@ -475,9 +479,9 @@ class Indirect { } public void testDontRecompileUnneeded() { - myFixture.addFileToProject('Base.groovy', 'class Base { }').virtualFile + myFixture.addFileToProject('Base.groovy', 'class Base { }') def foo = myFixture.addFileToProject('Foo.groovy', 'class Foo extends Base { }').virtualFile - myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo { }').virtualFile + myFixture.addFileToProject('Bar.groovy', 'class Bar extends Foo { }') def main = myFixture.addFileToProject('Main.groovy', 'class Main extends Bar { }').virtualFile assertEmpty make() long oldBaseStamp = findClassFile("Base").modificationStamp @@ -490,17 +494,34 @@ class Indirect { assert oldBaseStamp == findClassFile("Base").modificationStamp } + public void _testPartialCrossRecompile() { + def used = myFixture.addFileToProject('Used.groovy', 'class Used { }') + def java = myFixture.addFileToProject('Java.java', 'class Java { void foo(Used used) {} }') + def main = myFixture.addFileToProject('Main.groovy', 'class Main extends Java { }').virtualFile + + assertEmpty compileModule(myModule) + assertEmpty compileFiles(used.virtualFile, main) + assertEmpty compileModule(myModule) + assertEmpty compileModule(myModule) + + setFileText(used, 'class Used2 {}') + makeShouldFail() + assert findClassFile('Used') == null + assert findClassFile('Used2') == null + + setFileText(used, 'class Used3 {}') + setFileText(java, 'class Java { void foo(Used3 used) {} }') + assertEmpty make() + + assert findClassFile('Used2') == null + } + public static class IdeaModeTest extends GroovyCompilerTest { @Override protected boolean useJps() { false } } public static class JpsModeTest extends GroovyCompilerTest { @Override protected boolean useJps() { true } - - @Override - void testCorrectFailAndCorrect() { - super.testCorrectFailAndCorrect() - } } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java index 6f910e67db60..5dee59b7d0db 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java @@ -42,6 +42,7 @@ import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; import com.intellij.testFramework.fixtures.TempDirTestFixture; import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl; +import com.intellij.util.Consumer; import com.intellij.util.ObjectUtils; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.ui.UIUtil; @@ -243,6 +244,33 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC } protected List make() { + return runCompiler(new Consumer() { + @Override + public void consume(ErrorReportingCallback callback) { + CompilerManager.getInstance(getProject()).make(callback); + } + }); + } + + protected List compileModule(final Module module) { + return runCompiler(new Consumer() { + @Override + public void consume(ErrorReportingCallback callback) { + CompilerManager.getInstance(getProject()).compile(module, callback); + } + }); + } + + protected List compileFiles(final VirtualFile... files) { + return runCompiler(new Consumer() { + @Override + public void consume(ErrorReportingCallback callback) { + CompilerManager.getInstance(getProject()).compile(files, callback); + } + }); + } + + private List runCompiler(final Consumer runnable) { final Semaphore semaphore = new Semaphore(); semaphore.down(); final ErrorReportingCallback callback = new ErrorReportingCallback(semaphore); @@ -258,7 +286,7 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC assert ioFile.exists(); } } - CompilerManager.getInstance(getProject()).make(callback); + runnable.consume(callback); } catch (Exception e) { throw new RuntimeException(e); diff --git a/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java b/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java index 082bc604d7d7..469d4b64ed03 100644 --- a/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java +++ b/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java @@ -365,7 +365,7 @@ public class JUnitConfiguration extends ModuleBasedConfiguration classes, PsiMethod method) { Set patterns = new HashSet(); for (PsiClass pattern : classes) { - patterns.add(pattern.getQualifiedName()); + patterns.add(JavaExecutionUtil.getRuntimeQualifiedName(pattern)); } myData.setPatterns(patterns); myData.METHOD_NAME = method.getName(); diff --git a/plugins/junit/src/com/intellij/execution/junit/TestMethod.java b/plugins/junit/src/com/intellij/execution/junit/TestMethod.java index ab93fa1232b9..e84c0ff0bc7a 100644 --- a/plugins/junit/src/com/intellij/execution/junit/TestMethod.java +++ b/plugins/junit/src/com/intellij/execution/junit/TestMethod.java @@ -21,7 +21,10 @@ import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; -import com.intellij.psi.*; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiPackage; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.listeners.RefactoringElementAdapter; import com.intellij.refactoring.listeners.RefactoringElementListener; @@ -106,12 +109,14 @@ class TestMethod extends TestObject { PsiMethod testMethod, PsiPackage testPackage) { if (testMethod == null) return false; - final PsiClass containingClass = testMethod.getContainingClass(); - if (testClass == null && (containingClass == null || !containingClass.hasModifierProperty(PsiModifier.ABSTRACT))) return false; + if (testClass == null) return false; final JUnitConfiguration.Data data = configuration.getPersistentData(); + /*final PsiClass containingClass = testMethod.getContainingClass(); + if (testClass == null && (containingClass == null || !containingClass.hasModifierProperty(PsiModifier.ABSTRACT))) return false; + if (containingClass != null && containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) { return Comparing.equal(testMethod.getName(), data.getMethodName()); - } + }*/ return Comparing.equal(JavaExecutionUtil.getRuntimeQualifiedName(testClass), data.getMainClassName()) && Comparing.equal(testMethod.getName(), data.getMethodName()); diff --git a/plugins/junit/src/com/intellij/execution/junit/TestsPattern.java b/plugins/junit/src/com/intellij/execution/junit/TestsPattern.java index df090c198bf0..01527b71fde3 100644 --- a/plugins/junit/src/com/intellij/execution/junit/TestsPattern.java +++ b/plugins/junit/src/com/intellij/execution/junit/TestsPattern.java @@ -27,7 +27,6 @@ import com.intellij.execution.util.JavaParametersUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; @@ -108,9 +107,9 @@ public class TestsPattern extends TestObject { PsiClass testClass, PsiMethod testMethod, PsiPackage testPackage) { - if (testMethod != null && Comparing.strEqual(testMethod.getName(), configuration.getPersistentData().METHOD_NAME)) { + /*if (testMethod != null && Comparing.strEqual(testMethod.getName(), configuration.getPersistentData().METHOD_NAME)) { return true; - } + }*/ return false; } diff --git a/plugins/junit/src/com/intellij/execution/junit2/ui/model/TestProxyDescriptor.java b/plugins/junit/src/com/intellij/execution/junit2/ui/model/TestProxyDescriptor.java index 30fcb1f92321..d5ef71bdeed5 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/ui/model/TestProxyDescriptor.java +++ b/plugins/junit/src/com/intellij/execution/junit2/ui/model/TestProxyDescriptor.java @@ -17,45 +17,47 @@ package com.intellij.execution.junit2.ui.model; import com.intellij.execution.junit2.TestProxy; +import com.intellij.execution.testframework.ui.BaseTestProxyNodeDescriptor; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -class TestProxyDescriptor extends NodeDescriptor { +class TestProxyDescriptor extends BaseTestProxyNodeDescriptor { private static final Logger LOG = Logger.getInstance("#com.intellij.execution.junit2.ui.model.TestProxyDescriptor"); private static final int STATE_UP_TO_DATE = 0; private static final int STATE_OUT_OF_DATE = 1; private static final int STATE_UNKNOWN = 2; - private final TestProxy myTest; private int myTimestamp = -1; private int myLastChildCount = -1; //private int myNeedsUpdate = STATE_UNKNOWN; private int myLastMagnitude = -1; public TestProxyDescriptor(final Project project, final NodeDescriptor parentDescriptor, final TestProxy test) { - super(project, parentDescriptor); - myTest = test; - myTimestamp = myTest.getStateTimestamp(); - myLastChildCount = myTest.getChildCount(); + super(project, test, parentDescriptor); + myTimestamp = test.getStateTimestamp(); + myLastChildCount = test.getChildCount(); myName = test.toString(); } public boolean update() { - boolean needsUpdate = checkNeedsUpdate(); - myTimestamp = myTest.getStateTimestamp(); - myLastChildCount = myTest.getChildCount(); + final TestProxy test = getElement(); + + boolean needsUpdate = checkNeedsUpdate(test); + myTimestamp = test.getStateTimestamp(); + myLastChildCount = test.getChildCount(); return needsUpdate; } - private boolean checkNeedsUpdate() { + private boolean checkNeedsUpdate(TestProxy test) { + int needsUpdate = STATE_UP_TO_DATE; - if (myTest.getChildCount() != myLastChildCount) { + if (test.getChildCount() != myLastChildCount) { needsUpdate = STATE_OUT_OF_DATE; } - else if (myTest.getStateTimestamp() != myTimestamp) needsUpdate = STATE_UNKNOWN; + else if (test.getStateTimestamp() != myTimestamp) needsUpdate = STATE_UNKNOWN; if (needsUpdate == STATE_UNKNOWN) { - final int magnitude = myTest.getState().getMagnitude(); + final int magnitude = test.getState().getMagnitude(); needsUpdate = magnitude == myLastMagnitude ? STATE_UP_TO_DATE : STATE_OUT_OF_DATE; myLastMagnitude = magnitude; } @@ -66,8 +68,4 @@ class TestProxyDescriptor extends NodeDescriptor { LOG.error(String.valueOf(needsUpdate)); return true; } - - public Object getElement() { - return myTest; - } } diff --git a/plugins/junit/src/com/intellij/execution/junit2/ui/model/TestTreeBuilder.java b/plugins/junit/src/com/intellij/execution/junit2/ui/model/TestTreeBuilder.java index f26d6aa0a1cf..e51fafe2e1e3 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/ui/model/TestTreeBuilder.java +++ b/plugins/junit/src/com/intellij/execution/junit2/ui/model/TestTreeBuilder.java @@ -16,8 +16,8 @@ package com.intellij.execution.junit2.ui.model; -import com.intellij.execution.junit2.events.TestEvent; import com.intellij.execution.junit2.TestProxy; +import com.intellij.execution.junit2.events.TestEvent; import com.intellij.execution.junit2.ui.properties.JUnitConsoleProperties; import com.intellij.execution.testframework.AbstractTestProxy; import com.intellij.execution.testframework.TestTreeView; @@ -25,9 +25,6 @@ import com.intellij.execution.testframework.ui.AbstractTestTreeBuilder; import com.intellij.ide.util.treeView.IndexComparator; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.util.StatusBarProgress; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -41,8 +38,9 @@ public class TestTreeBuilder extends AbstractTestTreeBuilder { private static final Logger LOG = Logger.getInstance("#com.intellij.execution.junit2.ui.model.TestTreeBuilder"); private JUnitRunningModel myModel; - private final JUnitAdapter myListener = new JUnitAdapter(){ + private final JUnitAdapter myListener = new JUnitAdapter() { private final Collection myNodesToUpdate = new HashSet(); + public void onEventsDispatched(final List events) { for (final TestEvent event : events) { final TestProxy testSubtree = (TestProxy)event.getTestSubtree(); @@ -75,18 +73,11 @@ public class TestTreeBuilder extends AbstractTestTreeBuilder { treeStructure.setSpecialNode(new SpecialNode(this, model)); myModel = model; myModel.addListener(myListener); - init(tree, new DefaultTreeModel(new DefaultMutableTreeNode(treeStructure.createDescriptor(model.getRoot(), null))), treeStructure, IndexComparator.INSTANCE, true); + init(tree, new DefaultTreeModel(new DefaultMutableTreeNode(treeStructure.createDescriptor(model.getRoot(), null))), treeStructure, + IndexComparator.INSTANCE, true); initRootNode(); } - protected boolean isSmartExpand() { - return false; - } - - protected boolean isAlwaysShowPlus(final NodeDescriptor nodeDescriptor) { - return false; - } - protected boolean isAutoExpandNode(final NodeDescriptor nodeDescriptor) { return nodeDescriptor.getElement() == myModel.getRoot(); } @@ -96,7 +87,7 @@ public class TestTreeBuilder extends AbstractTestTreeBuilder { DefaultMutableTreeNode node = getNodeForElement(test); if (node != null) { if (node.getParent() != null) { - expandNodeChildren((DefaultMutableTreeNode) node.getParent()); + expandNodeChildren((DefaultMutableTreeNode)node.getParent()); node = getNodeForElement(test); } return node; @@ -112,9 +103,4 @@ public class TestTreeBuilder extends AbstractTestTreeBuilder { } return node; } - - @NotNull - protected ProgressIndicator createProgressIndicator() { - return new StatusBarProgress(); - } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunnerParametersConfigurable.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunnerParametersConfigurable.java index 36a845b23737..60c0e8227f25 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunnerParametersConfigurable.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunnerParametersConfigurable.java @@ -64,53 +64,55 @@ public abstract class MavenRunnerParametersConfigurable implements Configurable, } }); - MyCompletionProvider profilesCompletionProvider = new MyCompletionProvider(project) { - @Override - protected void addVariants(@NotNull CompletionResultSet result, MavenProjectsManager manager) { - for (String profile : manager.getAvailableProfiles()) { - result.addElement(LookupElementBuilder.create(profile)); + if (!project.isDefault()) { + MyCompletionProvider profilesCompletionProvider = new MyCompletionProvider(project) { + @Override + protected void addVariants(@NotNull CompletionResultSet result, MavenProjectsManager manager) { + for (String profile : manager.getAvailableProfiles()) { + result.addElement(LookupElementBuilder.create(profile)); + } } - } - }; - - profilesComponent.setComponent(profilesCompletionProvider.createEditor(project)); + }; - MyCompletionProvider goalsCompletionProvider = new MyCompletionProvider(project) { - - private volatile List myCachedElements; - - @Override - protected void addVariants(@NotNull CompletionResultSet result, MavenProjectsManager manager) { - List cachedElements = myCachedElements; - if (cachedElements == null) { - Set goals = new HashSet(); - goals.addAll(MavenConstants.PHASES); + profilesComponent.setComponent(profilesCompletionProvider.createEditor(project)); - for (MavenProject mavenProject : manager.getProjects()) { - for (MavenPlugin plugin : mavenProject.getPlugins()) { - MavenPluginInfo pluginInfo = MavenArtifactUtil.readPluginInfo(manager.getLocalRepository(), plugin.getMavenId()); - if (pluginInfo != null) { - for (MavenPluginInfo.Mojo mojo : pluginInfo.getMojos()) { - goals.add(mojo.getDisplayName()); + MyCompletionProvider goalsCompletionProvider = new MyCompletionProvider(project) { + + private volatile List myCachedElements; + + @Override + protected void addVariants(@NotNull CompletionResultSet result, MavenProjectsManager manager) { + List cachedElements = myCachedElements; + if (cachedElements == null) { + Set goals = new HashSet(); + goals.addAll(MavenConstants.PHASES); + + for (MavenProject mavenProject : manager.getProjects()) { + for (MavenPlugin plugin : mavenProject.getPlugins()) { + MavenPluginInfo pluginInfo = MavenArtifactUtil.readPluginInfo(manager.getLocalRepository(), plugin.getMavenId()); + if (pluginInfo != null) { + for (MavenPluginInfo.Mojo mojo : pluginInfo.getMojos()) { + goals.add(mojo.getDisplayName()); + } } } } + + cachedElements = new ArrayList(goals.size()); + for (String goal : goals) { + cachedElements.add(LookupElementBuilder.create(goal).setIcon(MavenIcons.PHASE_ICON)); + } + + myCachedElements = cachedElements; } - cachedElements = new ArrayList(goals.size()); - for (String goal : goals) { - cachedElements.add(LookupElementBuilder.create(goal).setIcon(MavenIcons.PHASE_ICON)); - } - - myCachedElements = cachedElements; + result.addAllElements(cachedElements); } + }; + + goalsComponent.setComponent(goalsCompletionProvider.createEditor(project)); + } - result.addAllElements(cachedElements); - } - }; - - goalsComponent.setComponent(goalsCompletionProvider.createEditor(project)); - setAnchor(profilesComponent.getLabel()); } diff --git a/plugins/properties/src/com/intellij/lang/properties/xml/XmlPropertiesIndex.java b/plugins/properties/src/com/intellij/lang/properties/xml/XmlPropertiesIndex.java index a0473e9fa33b..827c08987a22 100644 --- a/plugins/properties/src/com/intellij/lang/properties/xml/XmlPropertiesIndex.java +++ b/plugins/properties/src/com/intellij/lang/properties/xml/XmlPropertiesIndex.java @@ -5,10 +5,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.indexing.*; -import com.intellij.util.io.DataExternalizer; -import com.intellij.util.io.EnumeratorStringDescriptor; -import com.intellij.util.io.IOUtil; -import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.io.*; import com.intellij.util.xml.NanoXmlUtil; import net.n3.nanoxml.StdXMLReader; import org.jetbrains.annotations.NotNull; @@ -91,7 +88,7 @@ public class XmlPropertiesIndex extends FileBasedIndexExtension - diff --git a/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/HttpClientTransport.java b/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/HttpClientTransport.java deleted file mode 100644 index 669fd13e0bb9..000000000000 --- a/plugins/tasks/jira-connector/src/main/java/com/intellij/tasks/jira/HttpClientTransport.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.intellij.tasks.jira; - -import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.xmlrpc.XmlRpc; -import org.apache.xmlrpc.XmlRpcClientException; -import org.apache.xmlrpc.XmlRpcTransport; - -import java.io.IOException; -import java.io.InputStream; - -public class HttpClientTransport implements XmlRpcTransport { - - private final String myUrl; - private final HttpClient myHttpClient; - private final Header myUserAgentHeader = new Header("User-Agent", XmlRpc.version); - private PostMethod myMethod; - - public HttpClientTransport(String url, HttpClient httpClient) { - myUrl = url; - myHttpClient = httpClient; - } - - public InputStream sendXmlRpc(byte[] request) throws IOException, XmlRpcClientException { - myMethod = new PostMethod(myUrl); - myMethod.setRequestHeader(new Header("Content-Type", "text/xml")); - myMethod.setRequestHeader(myUserAgentHeader); - myMethod.setRequestEntity(new ByteArrayRequestEntity(request)); - - configureMethod(myMethod); - - myHttpClient.executeMethod(myMethod); - return myMethod.getResponseBodyAsStream(); - } - - protected void configureMethod(HttpMethod method) { - } - - public void endClientRequest() throws XmlRpcClientException { - myMethod.releaseConnection(); - } -} diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepository.java index 5ce6d6c4b299..a4cb3f78ca89 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/trac/TracRepository.java @@ -23,10 +23,7 @@ import com.intellij.tasks.impl.BaseRepositoryImpl; import com.intellij.tasks.impl.LocalTaskImpl; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.annotations.Tag; -import org.apache.xmlrpc.CommonsXmlRpcTransport; -import org.apache.xmlrpc.XmlRpcClient; -import org.apache.xmlrpc.XmlRpcException; -import org.apache.xmlrpc.XmlRpcRequest; +import org.apache.xmlrpc.*; import org.jetbrains.annotations.Nullable; import java.io.IOException; @@ -41,6 +38,10 @@ import java.util.*; @SuppressWarnings({"UseOfObsoleteCollectionType", "unchecked"}) public class TracRepository extends BaseRepositoryImpl { + static { + XmlRpc.setDefaultInputEncoding("UTF-8"); + } + private String myDefaultSearch = "status!=closed&owner={username}&summary~={query}"; private Boolean myMaxSupported; @@ -57,10 +58,8 @@ public class TracRepository extends BaseRepositoryImpl { String search = myDefaultSearch + "&max=" + max; if (myMaxSupported == null) { try { + myMaxSupported = true; result = runQuery(query, transport, client, search); - if (result != null) { - myMaxSupported = true; - } } catch (XmlRpcException e) { if (e.getMessage().contains("t.max")) { @@ -98,7 +97,7 @@ public class TracRepository extends BaseRepositoryImpl { } private XmlRpcClient getRpcClient() throws MalformedURLException { - return new XmlRpcClient(new URL(getUrl())); + return new XmlRpcClient(getUrl()); } @Override diff --git a/plugins/tasks/tasks-tests/tasks-tests.iml b/plugins/tasks/tasks-tests/tasks-tests.iml index f59b8dfccb7e..668e75245368 100644 --- a/plugins/tasks/tasks-tests/tasks-tests.iml +++ b/plugins/tasks/tasks-tests/tasks-tests.iml @@ -14,6 +14,8 @@ + +
diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/integration/TracIntegrationTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/integration/TracIntegrationTest.java new file mode 100644 index 000000000000..74f80ff4c818 --- /dev/null +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/integration/TracIntegrationTest.java @@ -0,0 +1,37 @@ +/* + * 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.tasks.integration; + +import com.intellij.tasks.Task; +import com.intellij.tasks.TaskManagerTestCase; +import com.intellij.tasks.trac.TracRepository; + +/** + * @author Dmitry Avdeev + * Date: 1/25/12 + */ +public abstract class TracIntegrationTest extends TaskManagerTestCase { + + public void testTracEncoding() throws Exception { + + TracRepository repository = new TracRepository(); + repository.setUseHttpAuthentication(true); + + Task task = repository.findTask("5358"); + assertNotNull(task); + assertEquals("Artikel k\u00f6nnen nicht in den Warenkorb gelegt werden", task.getSummary()); + } +} diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java index 4564aa0b66a3..9021ecea90b2 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java @@ -261,7 +261,7 @@ public class TestNGConfiguration extends ModuleBasedConfiguration classes, PsiMethod method) { Set patterns = new HashSet(); for (PsiClass pattern : classes) { - patterns.add(pattern.getQualifiedName()); + patterns.add(JavaExecutionUtil.getRuntimeQualifiedName(pattern)); } data.setPatterns(patterns); data.METHOD_NAME = method.getName(); diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNodeDescriptor.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNodeDescriptor.java index e0a06884dbfc..f9b78b7a9407 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/model/TestNodeDescriptor.java +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNodeDescriptor.java @@ -15,33 +15,15 @@ */ package com.theoryinpractice.testng.model; +import com.intellij.execution.testframework.ui.BaseTestProxyNodeDescriptor; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.project.Project; /** * @author Hani Suleiman Date: Jul 28, 2005 Time: 10:44:26 PM */ -public class TestNodeDescriptor extends NodeDescriptor -{ - private final TestProxy node; - - public TestNodeDescriptor(Project project, TestProxy node, NodeDescriptor parent) { - super(project, parent); - this.node = node; - myName = node.getName(); - } - - @Override - public boolean update() { - return false; - } - - @Override - public TestProxy getElement() { - return node; - } - - public boolean expandOnDoubleClick() { - return !node.isResult(); - } +public class TestNodeDescriptor extends BaseTestProxyNodeDescriptor { + public TestNodeDescriptor(Project project, TestProxy node, NodeDescriptor parent) { + super(project, node, parent); + } } diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestTreeBuilder.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestTreeBuilder.java index 1bf037826de8..24b45f076088 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/model/TestTreeBuilder.java +++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestTreeBuilder.java @@ -27,36 +27,26 @@ import javax.swing.tree.DefaultTreeModel; /** * @author Hani Suleiman Date: Jul 28, 2005 Time: 10:49:36 PM */ -public class TestTreeBuilder extends AbstractTestTreeBuilder -{ - public TestTreeBuilder(JTree tree, AbstractTreeStructure structure) { - super(tree, new DefaultTreeModel(new DefaultMutableTreeNode(structure.getRootElement())), structure, IndexComparator.INSTANCE); - initRootNode(); - } +public class TestTreeBuilder extends AbstractTestTreeBuilder { + public TestTreeBuilder(JTree tree, AbstractTreeStructure structure) { + super(tree, new DefaultTreeModel(new DefaultMutableTreeNode(structure.getRootElement())), structure, IndexComparator.INSTANCE); + initRootNode(); + } - @Override - protected boolean isSmartExpand() { - return false; - } + @Override + protected boolean isAutoExpandNode(NodeDescriptor descriptor) { + return descriptor.getElement() == getTreeStructure().getRootElement(); + } - @Override - protected boolean isAlwaysShowPlus(NodeDescriptor descriptor) { - return false; + public void addItem(TestProxy parent, TestProxy proxy) { + parent.addChild(proxy); + DefaultMutableTreeNode parentNode = getNodeForElement(parent); + if (parentNode != null) { + updateSubtree(parentNode); } + } - @Override - protected boolean isAutoExpandNode(NodeDescriptor descriptor) { - return descriptor.getElement() == getTreeStructure().getRootElement(); - } - - public void addItem(TestProxy parent, TestProxy proxy) { - parent.addChild(proxy); - DefaultMutableTreeNode parentNode = getNodeForElement(parent); - if (parentNode != null) - updateSubtree(parentNode); - } - - public TestProxy getRoot() { - return (TestProxy) getTreeStructure().getRootElement(); - } + public TestProxy getRoot() { + return (TestProxy)getTreeStructure().getRootElement(); + } } diff --git a/plugins/testng/src/com/theoryinpractice/testng/ui/TestNGResults.java b/plugins/testng/src/com/theoryinpractice/testng/ui/TestNGResults.java index a6c33689e7b5..c75ec170d5bf 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/ui/TestNGResults.java +++ b/plugins/testng/src/com/theoryinpractice/testng/ui/TestNGResults.java @@ -214,7 +214,8 @@ public class TestNGResults extends TestResultsPanel implements TestFrameworkRunn final String testMethodDescriptor = result.getTestClass() + result.getMethod(); if (startedMethods.contains(testMethodDescriptor)) { total++; - } else { + } + else { startedMethods.add(testMethodDescriptor); } animator.setCurrentTestCase(proxy); @@ -241,13 +242,15 @@ public class TestNGResults extends TestResultsPanel implements TestFrameworkRunn final PsiElement element = getPackageClassNodeFor(result).getPsiElement(); if (element instanceof PsiClass) { final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction( - new Computable() { - public PsiMethod[] compute() { - return ((PsiClass)element).findMethodsByName(result.getMethod(), true); - } + new Computable() { + public PsiMethod[] compute() { + return ((PsiClass)element).findMethodsByName(result.getMethod(), true); } + } ); - if (methods.length > 0 && methods[0] != null && !AnnotationUtil.isAnnotated(methods[0], Arrays.asList(TestNGUtil.CONFIG_ANNOTATIONS_FQN))) { + if (methods.length > 0 && + methods[0] != null && + !AnnotationUtil.isAnnotated(methods[0], Arrays.asList(TestNGUtil.CONFIG_ANNOTATIONS_FQN))) { for (List proxies : started.values()) { if (proxies != null) { for (TestProxy proxy : proxies) { @@ -283,7 +286,8 @@ public class TestNGResults extends TestResultsPanel implements TestFrameworkRunn if (failedToStart != null) { failedToStart.addChild(testCase); failedToStart.setTearDownFailure(true); - } else { + } + else { failedToStart = testCase; } } @@ -294,7 +298,8 @@ public class TestNGResults extends TestResultsPanel implements TestFrameworkRunn if (result.getResult() == MessageHelper.FAILED_TEST) { myStatusLine.setStatusColor(ColorProgressBar.RED); myStatus = MessageHelper.FAILED_TEST; - } else if (result.getResult() == MessageHelper.SKIPPED_TEST && myStatus == MessageHelper.PASSED_TEST) { + } + else if (result.getResult() == MessageHelper.SKIPPED_TEST && myStatus == MessageHelper.PASSED_TEST) { myStatus = MessageHelper.SKIPPED_TEST; } myStatusLine.setFraction((double)count / total); @@ -393,6 +398,11 @@ public class TestNGResults extends TestResultsPanel implements TestFrameworkRunn return tree; } + @Override + public TestTreeBuilder getTreeBuilder() { + return treeBuilder; + } + public boolean hasTestSuites() { return rootNode.getChildren().size() > 0; } diff --git a/xml/dom-impl/src/com/intellij/util/xml/DomFileIndex.java b/xml/dom-impl/src/com/intellij/util/xml/DomFileIndex.java index d5b01c6a4835..38b794a47e03 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/DomFileIndex.java +++ b/xml/dom-impl/src/com/intellij/util/xml/DomFileIndex.java @@ -22,12 +22,12 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.indexing.*; import com.intellij.util.io.EnumeratorStringDescriptor; import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.io.UnsyncByteArrayInputStream; import com.intellij.util.xml.impl.DomApplicationComponent; import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; -import java.io.ByteArrayInputStream; import java.util.Arrays; import java.util.Collections; import java.util.Map; @@ -50,7 +50,7 @@ public class DomFileIndex extends ScalarIndexExtension{ @NotNull public Map map(final FileContent inputData) { final Set namespaces = new THashSet(); - final XmlFileHeader header = NanoXmlUtil.parseHeader(new ByteArrayInputStream(inputData.getContent())); + final XmlFileHeader header = NanoXmlUtil.parseHeader(new UnsyncByteArrayInputStream(inputData.getContent())); ContainerUtil.addIfNotNull(header.getPublicId(), namespaces); ContainerUtil.addIfNotNull(header.getSystemId(), namespaces); ContainerUtil.addIfNotNull(header.getRootTagNamespace(), namespaces); diff --git a/xml/dom-openapi/src/com/intellij/util/xml/model/impl/DomModelImpl.java b/xml/dom-openapi/src/com/intellij/util/xml/model/impl/DomModelImpl.java index 2551b446df90..a3ef1b3a706c 100644 --- a/xml/dom-openapi/src/com/intellij/util/xml/model/impl/DomModelImpl.java +++ b/xml/dom-openapi/src/com/intellij/util/xml/model/impl/DomModelImpl.java @@ -17,6 +17,7 @@ package com.intellij.util.xml.model.impl; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; import com.intellij.psi.xml.XmlFile; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; diff --git a/xml/impl/src/com/intellij/xml/index/XmlNamespaceIndex.java b/xml/impl/src/com/intellij/xml/index/XmlNamespaceIndex.java index 8cd41a372f2e..5af871e438af 100644 --- a/xml/impl/src/com/intellij/xml/index/XmlNamespaceIndex.java +++ b/xml/impl/src/com/intellij/xml/index/XmlNamespaceIndex.java @@ -24,10 +24,10 @@ import com.intellij.util.indexing.FileBasedIndex; import com.intellij.util.indexing.FileContent; import com.intellij.util.indexing.ID; import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.UnsyncByteArrayInputStream; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.ByteArrayInputStream; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -67,7 +67,7 @@ public class XmlNamespaceIndex extends XmlIndex { return new DataIndexer() { @NotNull public Map map(final FileContent inputData) { - final String ns = XsdNamespaceBuilder.computeNamespace(new ByteArrayInputStream(inputData.getContent())); + final String ns = XsdNamespaceBuilder.computeNamespace(new UnsyncByteArrayInputStream(inputData.getContent())); final HashMap map = new HashMap(2); if (ns != null) { map.put(ns, ""); diff --git a/xml/impl/src/com/intellij/xml/index/XmlTagNamesIndex.java b/xml/impl/src/com/intellij/xml/index/XmlTagNamesIndex.java index b18d184ca698..a8d1901574a3 100644 --- a/xml/impl/src/com/intellij/xml/index/XmlTagNamesIndex.java +++ b/xml/impl/src/com/intellij/xml/index/XmlTagNamesIndex.java @@ -20,9 +20,9 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.HashMap; import com.intellij.util.indexing.*; import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.UnsyncByteArrayInputStream; import org.jetbrains.annotations.NotNull; -import java.io.ByteArrayInputStream; import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -50,7 +50,7 @@ public class XmlTagNamesIndex extends XmlIndex { return new DataIndexer() { @NotNull public Map map(final FileContent inputData) { - final Collection tags = XsdTagNameBuilder.computeTagNames(new ByteArrayInputStream(inputData.getContent())); + final Collection tags = XsdTagNameBuilder.computeTagNames(new UnsyncByteArrayInputStream(inputData.getContent())); if (tags != null && !tags.isEmpty()) { final HashMap map = new HashMap(tags.size()); for (String tag : tags) { diff --git a/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/RncParserDefinition.java b/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/RncParserDefinition.java index ac48d70d1a94..431b40e345b0 100644 --- a/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/RncParserDefinition.java +++ b/xml/relaxng/src/org/intellij/plugins/relaxNG/compact/RncParserDefinition.java @@ -43,6 +43,7 @@ import org.jetbrains.annotations.NotNull; */ public class RncParserDefinition implements ParserDefinition { public static final IFileElementType FILE_ELEMENT_TYPE = new IFileElementType(RngCompactLanguage.INSTANCE); + private static final TokenSet myCommentTypes = TokenSet.orSet(RncTokenTypes.COMMENTS, RncTokenTypes.DOC_TOKENS); @NotNull public Lexer createLexer(Project project) { @@ -64,7 +65,7 @@ public class RncParserDefinition implements ParserDefinition { @NotNull public TokenSet getCommentTokens() { - return TokenSet.orSet(RncTokenTypes.COMMENTS, RncTokenTypes.DOC_TOKENS); + return myCommentTypes; } @NotNull