From c3276bd56e6c0e125fab714fee0843fd9f8ba988 Mon Sep 17 00:00:00 2001 From: anna Date: Fri, 20 Jul 2012 15:43:06 +0200 Subject: [PATCH 01/40] lambda: return type checks initial --- .../src/com/intellij/psi/LambdaUtil.java | 58 ++++++++++++++- .../intellij/psi/PsiLambdaParameterType.java | 4 ++ .../highlighting/ReturnTypeCompatibility.java | 72 +++++++++++++++++++ .../lambda/params/FormalParams.java | 16 +++++ .../lambda/params/InferredParams.java | 25 +++++++ .../daemon/lambda/LambdaHighlightingTest.java | 4 ++ 6 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReturnTypeCompatibility.java diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index fcdbc9706fd9..d75d0147b886 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.psi; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.util.*; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; @@ -29,6 +30,8 @@ import java.util.List; * Date: 7/17/12 */ public class LambdaUtil { + private static final Logger LOG = Logger.getInstance("#" + LambdaUtil.class.getName()); + @Nullable public static String checkInterfaceFunctional(@NotNull PsiClass psiClass) { final List signatures = findFunctionCandidates(psiClass); @@ -140,6 +143,10 @@ public class LambdaUtil { else if (parent instanceof PsiVariable) { type = ((PsiVariable)parent).getType(); } + else if (parent instanceof PsiAssignmentExpression) { + final PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression(); + type = lExpression.getType(); + } else if (parent instanceof PsiExpressionList) { final PsiExpressionList expressionList = (PsiExpressionList)parent; final int lambdaIdx = ArrayUtil.find(expressionList.getExpressions(), lambdaExpression); @@ -173,7 +180,8 @@ public class LambdaUtil { public static boolean isAcceptable(PsiLambdaExpression lambdaExpression, final PsiType leftType) { final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(leftType); - final MethodSignature methodSignature = getFunction(resolveResult.getElement()); + final PsiClass psiClass = resolveResult.getElement(); + final MethodSignature methodSignature = getFunction(psiClass); if (methodSignature == null) return false; final PsiParameter[] lambdaParameters = lambdaExpression.getParameterList().getParameters(); final PsiType[] parameterTypes = methodSignature.getParameterTypes(); @@ -182,11 +190,57 @@ public class LambdaUtil { PsiParameter parameter = lambdaParameters[lambdaParamIdx]; final PsiTypeElement typeElement = parameter.getTypeElement(); if (typeElement != null) { - if (!typeElement.getType().equals(resolveResult.getSubstitutor().substitute(parameterTypes[lambdaParamIdx]))) { + final PsiType lambdaFormalType = typeElement.getType(); + final PsiType methodParameterType = parameterTypes[lambdaParamIdx]; + if (lambdaFormalType instanceof PsiPrimitiveType){ + if (methodParameterType instanceof PsiPrimitiveType) return methodParameterType.isAssignableFrom(lambdaFormalType); + return false; + } + + if (!resolveResult.getSubstitutor().substitute(methodParameterType).isAssignableFrom(lambdaFormalType)) { return false; } } } + PsiMethod method = null; + LOG.assertTrue(psiClass != null); + final PsiMethod[] methodsByName = psiClass.findMethodsByName(methodSignature.getName(), true); + for (PsiMethod psiMethod : methodsByName) { + if (MethodSignatureUtil.areSignaturesEqual(getMethodSignature(psiMethod, psiClass, psiMethod.getContainingClass()), methodSignature)) { + method = psiMethod; + break; + } + } + LOG.assertTrue(method != null); + PsiType methodReturnType = method.getReturnType(); + if (methodReturnType != null && methodReturnType != PsiType.VOID) { + methodReturnType = resolveResult.getSubstitutor().substitute(methodReturnType); + final PsiElement body = lambdaExpression.getBody(); + if (body instanceof PsiCodeBlock) { + final PsiCodeBlock block = (PsiCodeBlock)body; + for (PsiStatement statement : block.getStatements()) { + if (statement instanceof PsiReturnStatement) { + final PsiExpression returnValue = ((PsiReturnStatement)statement).getReturnValue(); + if (returnValue != null) { + if (!checkReturnTypeAssignability(returnValue.getType(), parameterTypes, lambdaExpression, methodReturnType)) return false; + } + } + } + } else if (body instanceof PsiExpression) { + return checkReturnTypeAssignability(((PsiExpression)body).getType(), parameterTypes, lambdaExpression, methodReturnType); + } + } return true; } + + private static boolean checkReturnTypeAssignability(PsiType lambdaReturnType, PsiType[] parameterTypes, PsiLambdaExpression lambdaExpression, PsiType methodReturnType) { + if (lambdaReturnType instanceof PsiLambdaParameterType) { + final PsiParameter parameter = ((PsiLambdaParameterType)lambdaReturnType).getParameter(); + final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter); + if (parameterIndex > -1) { + lambdaReturnType = parameterTypes[parameterIndex]; + } + } + return lambdaReturnType != null && methodReturnType.isAssignableFrom(lambdaReturnType); + } } diff --git a/java/java-psi-api/src/com/intellij/psi/PsiLambdaParameterType.java b/java/java-psi-api/src/com/intellij/psi/PsiLambdaParameterType.java index 64282ce78d69..de62f0ee8254 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiLambdaParameterType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiLambdaParameterType.java @@ -70,4 +70,8 @@ public class PsiLambdaParameterType extends PsiType { public PsiType[] getSuperTypes() { return PsiType.EMPTY_ARRAY; } + + public PsiParameter getParameter() { + return myParameter; + } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReturnTypeCompatibility.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReturnTypeCompatibility.java new file mode 100644 index 000000000000..b1d08e6f04ad --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/ReturnTypeCompatibility.java @@ -0,0 +1,72 @@ +class ReturnTypeIncompatibility { + + interface I1 { + T m(Integer x); + } + + interface I2 { + L m(Integer x); + } + + interface I3 { + void m(Integer x); + } + + static

void call(I1

i1) { + i1.m(1); + } + + static

void call(I2

i2) { + i2.m(2); + } + + static void call(I3 i3) { + i3.m(3); + } + + public static void main(String[] args) { + call(i-> {return i;}); + } +} + + +class ReturnTypeCompatibility { + + interface I1 { + T m(T x); + } + + interface I2 { + L m(L x); + } + + interface I3 { + void m(K x); + } + + static

void call(I1

i1) { + i1.m(null); + } + + static

void call(I2

i2) { + i2.m(null); + } + + static void call(I3 i3) { + i3.m(null); + } + + public static void main(String[] args) { + call(i-> {return i;}); + } +} + +class ReturnTypeChecks1 { + + interface I { + V m(K k); + } + + I accepted = i -> { return i; }; + I rejected = i -> { return i; }; +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/FormalParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/FormalParams.java index 1f31feafea35..65d6c977c161 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/FormalParams.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/FormalParams.java @@ -16,4 +16,20 @@ class Foo { bar((int i) -> {System.out.println(i);}); } void bar(I i){} +} + +class ReturnTypeCompatibility { + + interface I1 { + L m(L x); + } + + static

void call(I1

i2) { + i2.m(null); + } + + public static void main(String[] args) { + call((String i)->{ return i;}); + call((int i)->{ return i;}); + } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/InferredParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/InferredParams.java index af7bbc9b814c..2f64985b9b9a 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/InferredParams.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/InferredParams.java @@ -1,3 +1,5 @@ +import java.lang.Integer; + interface I { void m(int i); } @@ -18,6 +20,14 @@ class Foo { int i = ab; }; + { + A a1; + a1 = (ab)->{ + String s = ab; + int i = ab; + }; + } + A bazz() { bar((o) -> { String s = o; @@ -33,3 +43,18 @@ class Foo { void bar(A a){} } +class CastInference { + public interface I1 { + X m(); + } + public interface I2 { + X m(); + } + public static void foo(I1 s) {} + public static void foo(I2 s) {} + + public static void main(String[] args) { + foo((I1)() -> 42); + I1 i1 = (I1)() -> 42; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java index 73746e12b1fe..1def2c547e3c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java @@ -37,6 +37,10 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testReturnTypeCompatibility() throws Exception { + doTest(); + } + private void doTest() throws Exception { doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false); } From 01aefae9dbef53af1f456982876a963d9ecb7773 Mon Sep 17 00:00:00 2001 From: anna Date: Fri, 20 Jul 2012 16:04:37 +0200 Subject: [PATCH 02/40] lambda: use wildcard bound for inferred param --- .../src/com/intellij/psi/LambdaUtil.java | 9 ++++++++- .../lambda/params/InferredParams.java | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index d75d0147b886..d3c1ded4d928 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -123,7 +123,14 @@ public class LambdaUtil { if (methodSignature != null) { final PsiType[] types = methodSignature.getParameterTypes(); if (parameterIndex < types.length) { - return resolveResult.getSubstitutor().substitute(types[parameterIndex]); + final PsiType psiType = resolveResult.getSubstitutor().substitute(types[parameterIndex]); + if (psiType instanceof PsiWildcardType) { + final PsiType bound = ((PsiWildcardType)psiType).getBound(); + if (bound != null) { + return bound; + } + } + return psiType; } } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/InferredParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/InferredParams.java index 2f64985b9b9a..cb22cf98aefb 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/InferredParams.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/params/InferredParams.java @@ -57,4 +57,18 @@ class CastInference { foo((I1)() -> 42); I1 i1 = (I1)() -> 42; } +} + +class WildcardBoundsUsage { + interface I { + boolean foo(X x); + } + + public I bar(I predicate) { + return null; + } + + { + I i = bar(c -> c.compareTo('x') < 0); + } } \ No newline at end of file From 7f8362d02559fce784e1e344fb6af77097f55d40 Mon Sep 17 00:00:00 2001 From: anna Date: Fri, 20 Jul 2012 18:54:43 +0200 Subject: [PATCH 03/40] lambda: tests --- .../src/com/intellij/psi/LambdaUtil.java | 4 +- .../lambda/highlighting/Ambiguity1.java | 17 +++++++ .../highlighting/AmbiguityRawGenerics.java | 27 ++++++++++ .../lambda/highlighting/AmbiguityVarargs.java | 28 +++++++++++ .../lambda/highlighting/CastInContexts.java | 27 ++++++++++ .../lambda/highlighting/InContexts.java | 49 +++++++++++++++++++ .../highlighting/InferenceFromArgs.java | 29 +++++++++++ .../highlighting/NoInferenceResult.java | 17 +++++++ .../highlighting/TypeArgsConsistency.java | 17 +++++++ .../lambda/highlighting/WildcardBounds.java | 20 ++++++++ .../daemon/lambda/LambdaHighlightingTest.java | 38 ++++++++++++++ 11 files changed, 271 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityVarargs.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/CastInContexts.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InContexts.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InferenceFromArgs.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/NoInferenceResult.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/WildcardBounds.java diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index d3c1ded4d928..f6ac8d0d5636 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -204,7 +204,7 @@ public class LambdaUtil { return false; } - if (!resolveResult.getSubstitutor().substitute(methodParameterType).isAssignableFrom(lambdaFormalType)) { + if (!resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodParameterType)).isAssignableFrom(lambdaFormalType)) { return false; } } @@ -221,7 +221,7 @@ public class LambdaUtil { LOG.assertTrue(method != null); PsiType methodReturnType = method.getReturnType(); if (methodReturnType != null && methodReturnType != PsiType.VOID) { - methodReturnType = resolveResult.getSubstitutor().substitute(methodReturnType); + methodReturnType = resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodReturnType)); final PsiElement body = lambdaExpression.getBody(); if (body instanceof PsiCodeBlock) { final PsiCodeBlock block = (PsiCodeBlock)body; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java new file mode 100644 index 000000000000..87f4b168fede --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java @@ -0,0 +1,17 @@ +interface I1 { + void m(); +} + +interface I2 { + X m(); +} + +class Ambiguity1 { + + static void m(I1 i1) {} + static void m(I2 i2) {} + + { + m(()->{throw new AssertionError();}); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java new file mode 100644 index 000000000000..595787704437 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java @@ -0,0 +1,27 @@ +interface I { + void m(); +} + +interface I1 { + int m(); +} + +interface I2 { + String m(); +} + +interface I3 { + A m(); +} + +class AmbiguityRawGenerics { + + void foo(I s) { } + void foo(I1 s) { } + void foo(I2 s) { } + void foo(I3 s) { } + + void bar() { + foo(()-> { throw new RuntimeException(); }); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityVarargs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityVarargs.java new file mode 100644 index 000000000000..412cbe527c7a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityVarargs.java @@ -0,0 +1,28 @@ +interface I { + void m(); +} + +interface I1 { + void m(A a); +} + +interface I2 { + void m(A a1, A a2); +} + +interface IV { + void m(A... as); +} + +class AmbiguityVarargs { + void foo(I s) { } + void foo(I1 s) { } + void foo(I2 s) { } + void foo(IV s) { } + + void test() { + foo(()->{}); + foo((a1) -> {}); + foo((a1, a2)->{}); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/CastInContexts.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/CastInContexts.java new file mode 100644 index 000000000000..1f577f8ca40f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/CastInContexts.java @@ -0,0 +1,27 @@ +interface I { + void m(int x); +} + +interface I1 { + int m(); +} +class CastInContexts { + void m(I s) { } + + void assignment() { + I i1 = (I)(x-> { System.out.println(); }); + I i2 = (I)((x-> { System.out.println(); })); + } + + void method() { + m((I)(x-> { System.out.println(); })); + } + + I returnContext() { + return (I)(x -> { System.out.println(); }); + } + + { + int i = (int) ()-> 1; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InContexts.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InContexts.java new file mode 100644 index 000000000000..48b3960d2626 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InContexts.java @@ -0,0 +1,49 @@ +interface I { + void m(int x); +} +class InContexts { + void m(I s) { } + + void assignment() { + I s1 = (x-> { System.out.println(); }); + I s2 = ((x-> { System.out.println(); })); + } + + void method() { + m((x-> { System.out.println(); })); + m(((x-> { System.out.println(); }))); + } + + I returnContext() { + return (x -> {System.out.println();}); + } +} + +interface I1 { + B foo(A a); +} +class ValueLambdaInContext { + + void m1(I1 i) { } + void m2(I1> i) { } + + void exprMethod() { + m1(s1 -> 1); + m2(s1 -> s2 -> 1); + } + + void exprAssignment() { + I1 in1 = s1 -> 1; + I1> in2 = s1 -> s2 -> 1; + } + + void statementMethod() { + m1(s1 -> { return 1; }); + m2(s1 -> { return s2 -> { return 1; }; }); + } + + void statementAssignment() { + I1 in1 = s1 -> { return 1; }; + I1> in2 = s1 -> { return s2 -> { return 1; }; }; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InferenceFromArgs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InferenceFromArgs.java new file mode 100644 index 000000000000..6c130a75c8b3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/InferenceFromArgs.java @@ -0,0 +1,29 @@ +interface I { + public V put(K k, V v); +} + +interface A{} +interface B extends A{} + +interface SameArgsI { + T same(T a, T b); +} + +class InferenceFromArgs { + + private static void bar(A a, I i) { } + + void foo(B b) { + bar(b, (k, v) -> {int i = k; int j = v; return Math.max(i, j);}); + } + + public static SameArgsI max() { + return (a, b) -> b; + } +} + + + + + + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/NoInferenceResult.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/NoInferenceResult.java new file mode 100644 index 000000000000..fd89667dc6d4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/NoInferenceResult.java @@ -0,0 +1,17 @@ +interface I { + B foo(A a); +} +class NoInferenceResult { + + I m(I f) { return null; } + void m1(T t) { } + + void test() { + m((String s1) -> (String s2) -> s1 + s2); + + m((String s1) -> s1.length()); + m((String s1) -> s1); + + m1(() -> { }); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java new file mode 100644 index 000000000000..59a7aa5a8c77 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/TypeArgsConsistency.java @@ -0,0 +1,17 @@ +class TypeArgsConsistency { + + interface I { + T m(int i, int j); + } + + static void foo(I s) { } + + static I bar(I s) { return null; } + + { + I i1 = (i, j) -> i + j; + foo((i, j) -> i + j); + I i2 = bar((i, j) -> i + j); + I i3 = bar((i, j) -> "" + i + j); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/WildcardBounds.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/WildcardBounds.java new file mode 100644 index 000000000000..b18dbb369a4b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/WildcardBounds.java @@ -0,0 +1,20 @@ +class WildcardBounds { + + interface I { + T foo(T a, T b); + } + + void m1(I f1) {} + void m2(I f2) {} + void m3(I f3) {} + + I f1 = (a, b) -> a; + I f2 = (a, b) -> a; + I f3 = (a, b) -> a; + + { + m1((a, b) -> a); + m2((a, b) -> a); + m3((a, b) -> a); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java index 1def2c547e3c..342ab9da2066 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java @@ -40,6 +40,44 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase { public void testReturnTypeCompatibility() throws Exception { doTest(); } + + public void testTypeArgsConsistency() throws Exception { + doTest(); + } + + public void testWildcardBounds() throws Exception { + doTest(); + } + + public void testAmbiguity1() throws Exception { + doTest(); + } + + public void testAmbiguityVarargs() throws Exception { + doTest(); + } + + public void testAmbiguityRawGenerics() throws Exception { + doTest(); + } + + + + public void testNoInferenceResult() throws Exception { + doTest(); + } + + public void testInferenceFromArgs() throws Exception { + doTest(); + } + + public void testInContexts() throws Exception { + doTest(); + } + + public void testCastInContexts() throws Exception { + doTest(); + } private void doTest() throws Exception { doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false); From 5d3bc19c464b3adb9c318978b2f99fb9cffadc58 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Fri, 20 Jul 2012 22:29:07 +0400 Subject: [PATCH 04/40] update documentation hints on selection changes --- .../newStructureView/StructureViewComponent.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java b/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java index e03169ec2685..9b7decc6e5b1 100644 --- a/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java +++ b/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java @@ -44,10 +44,7 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.util.PsiUtilBase; -import com.intellij.ui.AutoScrollFromSourceHandler; -import com.intellij.ui.AutoScrollToSourceHandler; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.ui.TreeSpeedSearch; +import com.intellij.ui.*; import com.intellij.ui.treeStructure.Tree; import com.intellij.ui.treeStructure.actions.CollapseAllAction; import com.intellij.ui.treeStructure.actions.ExpandAllAction; @@ -132,7 +129,7 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre }; final DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode(treeStructure.getRootElement())); - myTree = new Tree(model); + myTree = new JBTreeWithHintProvider(model); myTree.setRootVisible(showRootNode); myTree.setShowsRootHandles(true); From 8d41b95c52b25adfe4383effbadfa63de016f5bc Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 18 Jul 2012 14:17:46 +0200 Subject: [PATCH 05/40] Assertion in test --- .../testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java index 50469b304ade..91cbc04fb418 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java @@ -114,6 +114,7 @@ public class FileWatcherTest extends PlatformLangTestCase { try { myConnection.disconnect(); myWatcher.shutdown(); + assertFalse(myWatcher.isOperational()); } finally { myFileSystem = null; From ff9bfb967243227cfd41b9b877ea3891a5943106 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 20 Jul 2012 20:28:46 +0200 Subject: [PATCH 06/40] One-shot fIle attributes reading --- .../openapi/vfs/impl/jar/JarHandlerBase.java | 2 +- .../openapi/vfs/ex/temp/TempFileSystem.java | 2 +- .../vfs/impl/win32/Win32LocalFileSystem.java | 6 +- .../vfs/local/SymlinkHandlingTest.java | 31 +--- .../openapi/util/io/FileAttributes.java | 116 ++++++++++---- .../openapi/util/io/FileSystemUtil.java | 139 ++++++++++------- .../util/src/com/intellij/util/BitUtil.java | 8 +- .../io/FileAttributesNio2ReadingTest.java | 44 ++++++ .../util/io/FileAttributesReadingTest.java | 147 ++++++++++++++++++ .../intellij/openapi/util/io/IoTestUtil.java | 53 +++++++ 10 files changed, 428 insertions(+), 120 deletions(-) create mode 100644 platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesNio2ReadingTest.java create mode 100644 platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java create mode 100644 platform/util/testSrc/com/intellij/openapi/util/io/IoTestUtil.java diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandlerBase.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandlerBase.java index e063890effdd..c75fa6105263 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandlerBase.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/JarHandlerBase.java @@ -308,7 +308,7 @@ public class JarHandlerBase { public FileAttributes getAttributes(@NotNull final VirtualFile file) { synchronized (lock) { final JarFile.JarEntry entry = convertToEntry(file); - return entry != null ? new FileAttributes(entry.isDirectory(), false, false, entry.getSize(), entry.getTime(), false) : null; + return entry != null ? new FileAttributes(entry.isDirectory(), false, false, false, entry.getSize(), entry.getTime(), false) : null; } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java index 1b9a3bae4eb9..7e81c94562c0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java @@ -382,6 +382,6 @@ public class TempFileSystem extends NewVirtualFileSystem { final FSItem item = convert(file); if (item == null) return null; final long length = item instanceof FSFile ? ((FSFile)item).myContent.length : 0; - return new FileAttributes(item.isDirectory(), false, false, length, item.myTimestamp, item.myWritable); + return new FileAttributes(item.isDirectory(), false, false, false, length, item.myTimestamp, item.myWritable); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32LocalFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32LocalFileSystem.java index 7bfd2404feda..0cec37f27b18 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32LocalFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/win32/Win32LocalFileSystem.java @@ -20,7 +20,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.impl.local.LocalFileSystemBase; import com.intellij.util.ArrayUtil; @@ -222,9 +221,10 @@ public class Win32LocalFileSystem extends LocalFileSystemBase { if (fileInfo == null) return null; final boolean isDirectory = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_DIRECTORY); - final boolean isSymlink = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_REPARSE_POINT); final boolean isSpecial = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_DEVICE); + final boolean isSymlink = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_REPARSE_POINT); + final boolean isHidden = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_HIDDEN); final boolean isWritable = !isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_READONLY); - return new FileAttributes(isDirectory, isSymlink, isSpecial, fileInfo.length, fileInfo.timestamp, isWritable); + return new FileAttributes(isDirectory, isSpecial, isSymlink, isHidden, fileInfo.length, fileInfo.timestamp, isWritable); } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java index 2a8cb8d5d453..1d5ba117808a 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java @@ -15,13 +15,10 @@ */ package com.intellij.openapi.vfs.local; -import com.intellij.execution.ExecutionException; -import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -30,6 +27,8 @@ import org.jetbrains.annotations.Nullable; import java.io.File; +import static com.intellij.openapi.util.io.IoTestUtil.createTempLink; + public class SymlinkHandlingTest extends LightPlatformLangTestCase { private LocalFileSystem myFileSystem; @@ -265,32 +264,6 @@ public class SymlinkHandlingTest extends LightPlatformLangTestCase { assertEquals(vSubChild, vSubChildRel); } - // todo[r.sh] use NIO2 API after migration to JDK 7 - public static File createTempLink(final String target, final String link) throws InterruptedException, ExecutionException { - final boolean isAbsolute = SystemInfo.isUnix && StringUtil.startsWithChar(link, '/') || - SystemInfo.isWindows && link.matches("^[c-zC-Z]:[/\\\\].*$"); - final File linkFile = isAbsolute ? new File(link) : new File(FileUtil.getTempDirectory(), link); - assertTrue(link, !linkFile.exists() || linkFile.delete()); - final File parentDir = linkFile.getParentFile(); - assertTrue("link=" + link + ", parent=" + parentDir, parentDir != null && (parentDir.isDirectory() || parentDir.mkdirs())); - - final GeneralCommandLine commandLine; - if (SystemInfo.isWindows) { - commandLine = new File(target).isDirectory() - ? new GeneralCommandLine("cmd", "/C", "mklink", "/D", linkFile.getAbsolutePath(), target) - : new GeneralCommandLine("cmd", "/C", "mklink", linkFile.getAbsolutePath(), target); - } - else { - commandLine = new GeneralCommandLine("ln", "-s", target, linkFile.getAbsolutePath()); - } - final int res = commandLine.createProcess().waitFor(); - assertEquals(commandLine.getCommandLineString(), 0, res); - - final File targetFile = new File(target); - assertEquals("target=" + target + ", link=" + linkFile, targetFile.exists(), linkFile.exists()); - return linkFile; - } - @Nullable private VirtualFile refreshAndFind(final File ioFile) { refresh(); diff --git a/platform/util/src/com/intellij/openapi/util/io/FileAttributes.java b/platform/util/src/com/intellij/openapi/util/io/FileAttributes.java index 24b6a1001fb2..9ed82712a354 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileAttributes.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileAttributes.java @@ -18,12 +18,23 @@ package com.intellij.openapi.util.io; import com.intellij.util.StringBuilderSpinAllocator; import org.intellij.lang.annotations.MagicConstant; +import static com.intellij.util.BitUtil.isSet; +import static com.intellij.util.BitUtil.notSet; + /** * @version 11.1 * @see FileSystemUtil#getAttributes(String) */ @SuppressWarnings("OctalInteger") public final class FileAttributes { + public static final int DIRECTORY = 0x0001; + public static final int SPECIAL = 0x0002; + public static final int SYM_LINK = 0x0010; + public static final int HIDDEN = 0x0100; + + @MagicConstant(flags = {DIRECTORY, SPECIAL, SYM_LINK, HIDDEN}) + public @interface FileType { } + public static final int OWNER_READ = 0400; public static final int OWNER_WRITE = 0200; public static final int OWNER_EXECUTE = 0100; @@ -34,52 +45,99 @@ public final class FileAttributes { public static final int OTHERS_WRITE = 0002; public static final int OTHERS_EXECUTE = 0001; - @MagicConstant(flags = { - OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, OTHERS_READ, OTHERS_WRITE, OTHERS_EXECUTE - }) + @MagicConstant(flags = {OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, OTHERS_READ, OTHERS_WRITE, OTHERS_EXECUTE}) public @interface Permissions { } - public final boolean isFile; - public final boolean isDirectory; - public final boolean isSymlink; - public final boolean isSpecial; - /** In bytes, 0 for symlinks and special files. */ + /** + * {@linkplain #DIRECTORY} and {@linkplain #SPECIAL} bits are mutually exclusive, none of them set means a regular file.
+ * {@linkplain #SYM_LINK} bit may be set along with above ones (which then denote a type of a link target).
+ * {@linkplain #HIDDEN} bit may be only set on Windows. + */ + @FileType + public final int type; + + /** + * In bytes, 0 for special files.
+ * For symlinks - length of a link target. + */ public final long length; - /** In milliseconds (note that actual resolution may be less accurate). */ + /** + * In milliseconds (actual resolution depends on a file system and may be less accurate).
+ * For symlinks - timestamp of a link target. + */ public final long lastModified; - /** UNIX permission bits (for Windows only OWNER_WRITE matters and OWNER_READ/EXECUTE are always set), or -1 if not supported. */ + /** + * UNIX permission bits (for Windows only OWNER_WRITE matters and OWNER_READ/EXECUTE are always set), or -1 if not supported.
+ * For symlinks - permissions of a link target. + */ @Permissions public final int permissions; - // todo: hidden flag (?) public FileAttributes(final boolean isDirectory, - final boolean isSymlink, final boolean isSpecial, + final boolean isSymlink, + final boolean isHidden, final long length, final long lastModified, - final boolean writable) { - this(isDirectory, isSymlink, isSpecial, length, lastModified, OWNER_READ | OWNER_EXECUTE | (writable ? OWNER_WRITE : 0)); + final boolean isWritable) { + this(type(isDirectory, isSpecial, isSymlink, isHidden), length, lastModified, OWNER_READ | OWNER_EXECUTE | (isWritable ? OWNER_WRITE : 0)); } public FileAttributes(final boolean isDirectory, - final boolean isSymlink, final boolean isSpecial, + final boolean isSymlink, final long length, final long lastModified, @Permissions final int permissions) { - this.isFile = !isDirectory && !isSymlink && !isSpecial; - this.isDirectory = isDirectory; - this.isSymlink = isSymlink; - this.isSpecial = isSpecial; - this.length = isSymlink || isSpecial ? 0 : length; + this(type(isDirectory, isSpecial, isSymlink, false), length, lastModified, permissions); + } + + private FileAttributes(@FileType final int type, + final long length, + final long lastModified, + @Permissions final int permissions) { + if (isSet(type, DIRECTORY) && isSet(type, SPECIAL)) { + throw new IllegalArgumentException("DIRECTORY and SPECIAL bits cannot be set simultaneously"); + } + + this.type = type; + this.length = length; this.lastModified = lastModified; this.permissions = permissions; } + @FileType + private static int type(final boolean isDirectory, final boolean isSpecial, final boolean isSymlink, final boolean isHidden) { + @FileType int type = 0; + if (isDirectory) type |= DIRECTORY; + if (isSpecial) type |= SPECIAL; + if (isSymlink) type |= SYM_LINK; + if (isHidden) type |= HIDDEN; + return type; + } + + /** Is {@code true} for files and symlinks to files (see {@linkplain #isRegularFile()}). */ + public boolean isFile() { + return notSet(type, DIRECTORY | SPECIAL); + } + + /** Is {@code true} for pure regular files only (see {@linkplain #isFile()}). */ + public boolean isRegularFile() { + return notSet(type, DIRECTORY | SPECIAL | SYM_LINK); + } + + public boolean isSymLink() { + return isSet(type, SYM_LINK); + } + + public boolean isWritable() { + return permissions == -1 || isSet(permissions, OWNER_WRITE) || isSet(permissions, GROUP_WRITE) || isSet(permissions, OTHERS_WRITE); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -87,10 +145,7 @@ public final class FileAttributes { final FileAttributes that = (FileAttributes)o; - if (isDirectory != that.isDirectory) return false; - if (isFile != that.isFile) return false; - if (isSpecial != that.isSpecial) return false; - if (isSymlink != that.isSymlink) return false; + if (type != that.type) return false; if (lastModified != that.lastModified) return false; if (permissions != that.permissions) return false; if (length != that.length) return false; @@ -100,10 +155,7 @@ public final class FileAttributes { @Override public int hashCode() { - int result = (isFile ? 1 : 0); - result = 31 * result + (isDirectory ? 1 : 0); - result = 31 * result + (isSymlink ? 1 : 0); - result = 31 * result + (isSpecial ? 1 : 0); + int result = type; result = 31 * result + (int)(length ^ (length >>> 32)); result = 31 * result + (int)(lastModified ^ (lastModified >>> 32)); result = 31 * result + permissions; @@ -115,10 +167,10 @@ public final class FileAttributes { final StringBuilder sb = StringBuilderSpinAllocator.alloc(); try { sb.append("[type:"); - if (isFile) sb.append('f'); - if (isDirectory) sb.append('d'); - if (isSymlink) sb.append('l'); - if (isSpecial) sb.append('!'); + if (isSet(type, DIRECTORY)) sb.append('d'); + else if (isSet(type, SPECIAL)) sb.append('!'); + else sb.append('f'); + if (isSet(type, SYM_LINK)) sb.append('l'); sb.append(" length:").append(length); 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 929902480269..b22a09e22d07 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java @@ -18,12 +18,14 @@ package com.intellij.openapi.util.io; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; import com.intellij.util.ArrayUtil; +import com.intellij.util.SystemProperties; import com.sun.jna.Library; import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.Pointer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import java.io.File; import java.lang.reflect.Array; @@ -31,47 +33,55 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; +import static com.intellij.util.BitUtil.isSet; +import static com.intellij.util.BitUtil.notSet; + /** * @version 11.1 */ public class FileSystemUtil { + public static final String FORCE_USE_NIO2_KEY = "idea.io.use.nio2"; + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.io.FileSystemUtil"); @NotNull - private static final Mediator ourMediator; - static { - Mediator mediator = null; + private static Mediator ourMediator = getMediator(); + + private static Mediator getMediator() { + final boolean forceUseNio2 = SystemProperties.getBooleanProperty(FORCE_USE_NIO2_KEY, false); + final String quickTestPath = SystemInfo.isWindows ? "C:\\" : "/"; // todo: move IdeaWin32 interface to this package, add mediator - if (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris || SystemInfo.isFreeBSD) { + if (!forceUseNio2 && (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris || SystemInfo.isFreeBSD)) { try { - mediator = new JnaUnixMediatorImpl(); - mediator.getAttributes("/"); // quick test + final Mediator mediator = new JnaUnixMediatorImpl(); + mediator.getAttributes(quickTestPath); + return mediator; } catch (Throwable t) { - LOG.error(t); - mediator = null; + LOG.warn(t); } } - if (mediator == null && SystemInfo.isJavaVersionAtLeast("1.7") && !"1.7.0-ea".equals(SystemInfo.JAVA_VERSION)) { + if (SystemInfo.isJavaVersionAtLeast("1.7") && !"1.7.0-ea".equals(SystemInfo.JAVA_VERSION)) { try { - mediator = new Jdk7MediatorImpl(); - mediator.getAttributes("/"); // quick test + final Mediator mediator = new Nio2MediatorImpl(); + mediator.getAttributes(quickTestPath); + return mediator; } catch (Throwable t) { - LOG.error(t); - mediator = null; + LOG.warn(t); } } - if (mediator == null) { - // todo: after introducing IdeaWin32 mediator, fail tests at this point, or issue a warning in production - mediator = new StandardMediatorImpl(); - } + // todo: after introducing IdeaWin32 mediator, fail tests at this point, or issue a warning in production + return new StandardMediatorImpl(); + } - ourMediator = mediator; + @TestOnly + static void resetMediator() { + ourMediator = getMediator(); } private FileSystemUtil() { } @@ -87,14 +97,21 @@ public class FileSystemUtil { return null; } + @SuppressWarnings("UnusedDeclaration") + @Nullable + public static FileAttributes getAttributes(@NotNull final File file) { + return getAttributes(file.getAbsolutePath()); + } + public static boolean isSymLink(@NotNull final String path) { if (SystemInfo.areSymLinksSupported) { final FileAttributes attributes = getAttributes(path); - return attributes != null && attributes.isSymlink; + return attributes != null && attributes.isSymLink(); } return false; } + @SuppressWarnings("UnusedDeclaration") public static boolean isSymLink(@NotNull final File file) { return isSymLink(file.getAbsolutePath()); } @@ -113,6 +130,7 @@ public class FileSystemUtil { return null; } + @SuppressWarnings("UnusedDeclaration") @Nullable public static String resolveSymLink(@NotNull final File file) { return resolveSymLink(file.getAbsolutePath()); @@ -123,6 +141,7 @@ public class FileSystemUtil { return attributes != null ? attributes.permissions : -1; } + @SuppressWarnings("UnusedDeclaration") public static int getPermissions(@NotNull final File file) { return getPermissions(file.getAbsolutePath()); } @@ -154,7 +173,7 @@ public class FileSystemUtil { // todo[r.sh] remove reflection after migration to JDK 7 @SuppressWarnings("OctalInteger") - private static class Jdk7MediatorImpl implements Mediator { + private static class Nio2MediatorImpl implements Mediator { private final Object myDefaultFileSystem; private final Method myGetPath; private final Method myIsSymbolicLink; @@ -163,8 +182,9 @@ public class FileSystemUtil { private final Method myReadAttributes; private final Method mySetAttribute; private final Method myToMillis; + private final String mySchema; - private Jdk7MediatorImpl() throws Exception { + private Nio2MediatorImpl() 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); @@ -189,29 +209,33 @@ public class FileSystemUtil { final Class fileTimeClass = Class.forName("java.nio.file.attribute.FileTime"); myToMillis = fileTimeClass.getMethod("toMillis"); myToMillis.setAccessible(true); + + mySchema = SystemInfo.isWindows ? "dos:*" : "posix:*"; } @Override public FileAttributes getAttributes(@NotNull final String path) throws Exception { try { final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY); + + Map attributes = (Map)myReadAttributes.invoke(null, pathObj, mySchema, myNoFollowLinkOptions); + final Boolean isSymbolicLink = (Boolean)attributes.get("isSymbolicLink"); + if (isSymbolicLink) { + attributes = (Map)myReadAttributes.invoke(null, pathObj, mySchema, myLinkOptions); + } + + final boolean isDirectory = (Boolean)attributes.get("isDirectory"); + final boolean isOther = (Boolean)attributes.get("isOther"); + final long size = (Long)attributes.get("size"); + final long lastModified = (Long)myToMillis.invoke(attributes.get("lastModifiedTime")); if (SystemInfo.isWindows) { - final Map attributes = (Map)myReadAttributes.invoke(null, pathObj, "dos:*", myNoFollowLinkOptions); - return new FileAttributes((Boolean)attributes.get("isDirectory"), - (Boolean)attributes.get("isSymbolicLink"), - (Boolean)attributes.get("isOther"), - (Long)attributes.get("size"), - (Long)myToMillis.invoke(attributes.get("lastModifiedTime")), - !(Boolean)attributes.get("readonly")); + final boolean isHidden = (Boolean)attributes.get("hidden"); + final boolean isWritable = !(Boolean)attributes.get("readonly"); + return new FileAttributes(isDirectory, isOther, isSymbolicLink, isHidden, size, lastModified, isWritable); } else { - final Map attributes = (Map)myReadAttributes.invoke(null, pathObj, "posix:*", myNoFollowLinkOptions); - return new FileAttributes((Boolean)attributes.get("isDirectory"), - (Boolean)attributes.get("isSymbolicLink"), - (Boolean)attributes.get("isOther"), - (Long)attributes.get("size"), - (Long)myToMillis.invoke(attributes.get("lastModifiedTime")), - decodePermissions(attributes.get("permissions"))); + final int permissions = decodePermissions(attributes.get("permissions")); + return new FileAttributes(isDirectory, isOther, isSymbolicLink, size, lastModified, permissions); } } catch (InvocationTargetException e) { @@ -302,7 +326,9 @@ public class FileSystemUtil { int PERM_MASK = 0777; int lstat(String path, Pointer stat); + int stat(String path, Pointer stat); int __lxstat64(int ver, String path, Pointer stat); + int __xstat64(int ver, String path, Pointer stat); int chmod(String path, int mode); } @@ -333,20 +359,26 @@ public class FileSystemUtil { @Override public synchronized FileAttributes getAttributes(@NotNull final String path) throws Exception { mySharedMem.clear(); - final int res = SystemInfo.isLinux ? myLibC.__lxstat64(0, path, mySharedMem) : myLibC.lstat(path, mySharedMem); - if (res == 0) { - final int mode = (SystemInfo.isLinux ? mySharedMem.getInt(myModeOffset) : mySharedMem.getShort(myModeOffset)) & LibC.S_MASK; - final boolean isDirectory = (mode & LibC.S_IFDIR) == LibC.S_IFDIR; - final boolean isSymlink = (mode & LibC.S_IFLNK) == LibC.S_IFLNK; - final boolean isSpecial = !isDirectory && !isSymlink && (mode & LibC.S_IFREG) == 0; - final long size = mySharedMem.getLong(mySizeOffset); - final long mTime1 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset) : mySharedMem.getLong(myTimeOffset); - final long mTime2 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset + 4) : mySharedMem.getLong(myTimeOffset + 8); - final long mTime = mTime1 * 1000 + mTime2 / 1000000; - @FileAttributes.Permissions final int permissions = mode & LibC.PERM_MASK; - return new FileAttributes(isDirectory, isSymlink, isSpecial, size, mTime, permissions); + int res = SystemInfo.isLinux ? myLibC.__lxstat64(0, path, mySharedMem) : myLibC.lstat(path, mySharedMem); + if (res != 0) return null; + + int mode = (SystemInfo.isLinux ? mySharedMem.getInt(myModeOffset) : mySharedMem.getShort(myModeOffset)) & LibC.S_MASK; + final boolean isSymlink = (mode & LibC.S_IFLNK) == LibC.S_IFLNK; + if (isSymlink) { + mySharedMem.clear(); + res = SystemInfo.isLinux ? myLibC.__xstat64(0, path, mySharedMem) : myLibC.stat(path, mySharedMem); + if (res != 0) return null; + mode = (SystemInfo.isLinux ? mySharedMem.getInt(myModeOffset) : mySharedMem.getShort(myModeOffset)) & LibC.S_MASK; } - return null; + + final boolean isDirectory = (mode & LibC.S_IFDIR) == LibC.S_IFDIR; + final boolean isSpecial = !isDirectory && (mode & LibC.S_IFREG) == 0; + final long size = mySharedMem.getLong(mySizeOffset); + final long mTime1 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset) : mySharedMem.getLong(myTimeOffset); + final long mTime2 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset + 4) : mySharedMem.getLong(myTimeOffset + 8); + final long mTime = mTime1 * 1000 + mTime2 / 1000000; + @FileAttributes.Permissions final int permissions = mode & LibC.PERM_MASK; + return new FileAttributes(isDirectory, isSpecial, isSymlink, size, mTime, permissions); } @Override @@ -364,6 +396,7 @@ public class FileSystemUtil { // from java.io.FileSystem private static final int BA_REGULAR = 0x02; private static final int BA_DIRECTORY = 0x04; + private static final int BA_HIDDEN = 0x08; private final Object myFileSystem; private final Method myGetBooleanAttributes; @@ -393,16 +426,18 @@ public class FileSystemUtil { if (myFileSystem != null) { final int flags = (Integer)myGetBooleanAttributes.invoke(myFileSystem, file); if (flags != 0) { - final boolean isDirectory = (flags & BA_DIRECTORY) != 0; - final boolean isSpecial = (flags & (BA_REGULAR | BA_DIRECTORY)) == 0; - return new FileAttributes(isDirectory, false, isSpecial, file.length(), file.lastModified(), file.canWrite()); + final boolean isDirectory = isSet(flags, BA_DIRECTORY); + final boolean isSpecial = notSet(flags, BA_REGULAR | BA_DIRECTORY); + final boolean isHidden = isSet(flags, BA_HIDDEN); + return new FileAttributes(isDirectory, isSpecial, false, isHidden, file.length(), file.lastModified(), file.canWrite()); } } else { if (file.exists()) { final boolean isDirectory = file.isDirectory(); final boolean isSpecial = !isDirectory && !file.isFile(); - return new FileAttributes(isDirectory, false, isSpecial, file.length(), file.lastModified(), file.canWrite()); + final boolean isHidden = file.isHidden(); + return new FileAttributes(isDirectory, isSpecial, false, isHidden, file.length(), file.lastModified(), file.canWrite()); } } diff --git a/platform/util/src/com/intellij/util/BitUtil.java b/platform/util/src/com/intellij/util/BitUtil.java index 36de2fb8dfbf..485d1ad21c06 100644 --- a/platform/util/src/com/intellij/util/BitUtil.java +++ b/platform/util/src/com/intellij/util/BitUtil.java @@ -19,7 +19,11 @@ package com.intellij.util; * @since 27.03.2012 */ public class BitUtil { - public static boolean isSet(final int mask, final int flag) { - return (mask & flag) == flag; + public static boolean isSet(final int value, final int flag) { + return (value & flag) == flag; + } + + public static boolean notSet(final int value, final int mask) { + return (value & mask) == 0; } } diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesNio2ReadingTest.java b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesNio2ReadingTest.java new file mode 100644 index 000000000000..ffb2674334b0 --- /dev/null +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesNio2ReadingTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.util.io; + +import com.intellij.openapi.util.SystemInfo; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +import static org.junit.Assume.assumeTrue; + +public class FileAttributesNio2ReadingTest extends FileAttributesReadingTest { + @BeforeClass + public static void setUpClass() throws Exception { + System.setProperty(FileSystemUtil.FORCE_USE_NIO2_KEY, "true"); + FileSystemUtil.resetMediator(); + } + + @AfterClass + public static void tearDownClass() throws Exception { + System.setProperty(FileSystemUtil.FORCE_USE_NIO2_KEY, ""); + FileSystemUtil.resetMediator(); + } + + @Override + @Before + public void setUp() throws Exception { + assumeTrue(SystemInfo.isJavaVersionAtLeast("1.7")); + super.setUp(); + } +} diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java new file mode 100644 index 000000000000..414ed48fcd15 --- /dev/null +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java @@ -0,0 +1,147 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.util.io; + +import com.intellij.openapi.util.SystemInfo; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; + +public class FileAttributesReadingTest { + private final byte[] myTestData = new byte[]{'t', 'e', 's', 't'}; + private File myTempDirectory; + + @Before + public void setUp() throws Exception { + myTempDirectory = FileUtil.createTempDirectory(getClass().getName(), ".tmp"); + } + + @After + public void tearDown() throws Exception { + if (myTempDirectory != null) { + FileUtil.delete(myTempDirectory); + } + } + + @Test + public void regularFile() throws Exception { + final File file = FileUtil.createTempFile(myTempDirectory, "test.", ".txt", true); + FileUtil.writeToFile(file, myTestData); + + final FileAttributes attributes = getAttributes(file); + assertEquals(0, attributes.type); + assertEquals(myTestData.length, attributes.length); + assertTimestampEquals(file.lastModified(), attributes.lastModified); + assertTrue(attributes.isWritable()); + } + + @Test + public void directory() throws Exception { + final File file = FileUtil.createTempDirectory(myTempDirectory, "test.", ".tmp"); + + final FileAttributes attributes = getAttributes(file); + assertEquals(FileAttributes.DIRECTORY, attributes.type); + assertEquals(file.length(), attributes.length); + assertTimestampEquals(file.lastModified(), attributes.lastModified); + assertTrue(attributes.isWritable()); + } + + @Test + public void special() throws Exception { + assumeTrue(SystemInfo.isUnix); + final File file = new File("/dev/null"); + + final FileAttributes attributes = getAttributes(file); + assertEquals(FileAttributes.SPECIAL, attributes.type); + assertEquals(0, attributes.length); + assertTrue(attributes.isWritable()); + } + + @Test + public void linkToFile() throws Exception { + final File file = FileUtil.createTempFile(myTempDirectory, "test.", ".txt", true); + FileUtil.writeToFile(file, myTestData); + assertTrue(file.setWritable(false, false)); + assertTrue(file.setLastModified(file.lastModified() - 5000)); + final File link = IoTestUtil.createTempLink(file.getPath(), new File(myTempDirectory, "link").getPath()); + + final FileAttributes attributes = getAttributes(link); + assertEquals(FileAttributes.SYM_LINK, attributes.type); + assertEquals(myTestData.length, attributes.length); + assertTimestampEquals(file.lastModified(), attributes.lastModified); + assertFalse(attributes.isWritable()); + + final String target = FileSystemUtil.resolveSymLink(link); + assertEquals(file.getPath(), target); + } + + @Test + public void doubleLink() throws Exception { + final File file = FileUtil.createTempFile(myTempDirectory, "test.", ".txt", true); + FileUtil.writeToFile(file, myTestData); + assertTrue(file.setWritable(false, false)); + assertTrue(file.setLastModified(file.lastModified() - 5000)); + final File link1 = IoTestUtil.createTempLink(file.getPath(), new File(myTempDirectory, "link1").getPath()); + final File link2 = IoTestUtil.createTempLink(link1.getPath(), new File(myTempDirectory, "link2").getPath()); + + final FileAttributes attributes = getAttributes(link2); + assertEquals(FileAttributes.SYM_LINK, attributes.type); + assertEquals(myTestData.length, attributes.length); + assertTimestampEquals(file.lastModified(), attributes.lastModified); + assertFalse(attributes.isWritable()); + + final String target = FileSystemUtil.resolveSymLink(link2); + assertEquals(file.getPath(), target); + } + + @Test + public void linkToDirectory() throws Exception { + final File file = FileUtil.createTempDirectory(myTempDirectory, "test.", ".tmp"); + assertTrue(file.setWritable(false, false)); + assertTrue(file.setLastModified(file.lastModified() - 5000)); + final File link = IoTestUtil.createTempLink(file.getPath(), new File(myTempDirectory, "link").getPath()); + + final FileAttributes attributes = getAttributes(link); + assertEquals(FileAttributes.DIRECTORY | FileAttributes.SYM_LINK, attributes.type); + assertEquals(file.length(), attributes.length); + assertTimestampEquals(file.lastModified(), attributes.lastModified); + assertFalse(attributes.isWritable()); + + final String target = FileSystemUtil.resolveSymLink(link); + assertEquals(file.getPath(), target); + } + + @NotNull + private static FileAttributes getAttributes(@NotNull final File file) { + final FileAttributes attributes = FileSystemUtil.getAttributes(file.getPath()); + assertNotNull(attributes); + System.out.println(attributes); + return attributes; + } + + private static void assertTimestampEquals(final long expected, final long actual) { + final long roundedExpected = (expected / 1000) * 1000; + final long roundedActual = (actual / 1000) * 1000; + assertEquals("expected: " + expected + ", actual: " + actual, + roundedExpected, roundedActual); + } +} diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/IoTestUtil.java b/platform/util/testSrc/com/intellij/openapi/util/io/IoTestUtil.java new file mode 100644 index 000000000000..da880c0560f3 --- /dev/null +++ b/platform/util/testSrc/com/intellij/openapi/util/io/IoTestUtil.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.util.io; + +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class IoTestUtil { + // todo[r.sh] use NIO2 API after migration to JDK 7 + public static File createTempLink(final String target, final String link) throws InterruptedException, IOException { + final boolean isAbsolute = SystemInfo.isUnix && StringUtil.startsWithChar(link, '/') || + SystemInfo.isWindows && link.matches("^[c-zC-Z]:[/\\\\].*$"); + final File linkFile = isAbsolute ? new File(link) : new File(FileUtil.getTempDirectory(), link); + assertTrue(link, !linkFile.exists() || linkFile.delete()); + final File parentDir = linkFile.getParentFile(); + assertTrue("link=" + link + ", parent=" + parentDir, parentDir != null && (parentDir.isDirectory() || parentDir.mkdirs())); + + final ProcessBuilder commandLine; + if (SystemInfo.isWindows) { + commandLine = new File(target).isDirectory() + ? new ProcessBuilder("cmd", "/C", "mklink", "/D", linkFile.getAbsolutePath(), target) + : new ProcessBuilder("cmd", "/C", "mklink", linkFile.getAbsolutePath(), target); + } + else { + commandLine = new ProcessBuilder("ln", "-s", target, linkFile.getAbsolutePath()); + } + final int res = commandLine.start().waitFor(); + assertEquals(commandLine.command().toString(), 0, res); + + final File targetFile = new File(target); + assertEquals("target=" + target + ", link=" + linkFile, targetFile.exists(), linkFile.exists()); + return linkFile; + } +} From 9b1773abce97583bff06293a547b2cb04361edbe Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Fri, 20 Jul 2012 20:49:10 +0200 Subject: [PATCH 07/40] more more test --- .../idea/svn16/SvnAuthenticationTest.java | 2504 ++++++++--------- 1 file changed, 1239 insertions(+), 1265 deletions(-) diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java index 665d5d34abac..23c28e13f958 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnAuthenticationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 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. @@ -15,1271 +15,1245 @@ */ package org.jetbrains.idea.svn16; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.Trinity; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.testFramework.PlatformTestCase; -import com.intellij.util.concurrency.Semaphore; -import com.intellij.util.ui.UIUtil; -import junit.framework.Assert; -import org.jetbrains.idea.svn.SvnAuthenticationManager; -import org.jetbrains.idea.svn.SvnConfiguration; -import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.auth.ProviderType; -import org.jetbrains.idea.svn.auth.SvnAuthenticationInteraction; -import org.jetbrains.idea.svn.auth.SvnAuthenticationListener; -import org.jetbrains.idea.svn.dialogs.SvnAuthenticationProvider; -import org.tmatesoft.svn.core.SVNErrorMessage; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.auth.*; -import org.tmatesoft.svn.core.internal.util.jna.SVNJNAUtil; -import org.tmatesoft.svn.core.internal.wc.SVNConfigFile; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; public class SvnAuthenticationTest extends PlatformTestCase { - private SvnAuthenticationManager myAuthenticationManager; - private TestInteraction myTestInteraction; - private final Object mySynchObject = new Object(); - private SvnTestInteractiveAuthentication myInteractiveProvider; - private SvnConfiguration myConfiguration; - - @Override - protected void runBareRunnable(Runnable runnable) throws Throwable { - runnable.run(); - } - - @Override - protected void setUp() throws Exception { - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - try { - SvnAuthenticationTest.super.setUp(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - }); - - - myConfiguration = SvnConfiguration.getInstance(myProject); - final String configPath = myProject.getBaseDir().getPath() + File.separator + "Subversion"; - myConfiguration.setConfigurationDirectory(configPath); - - final File configFile = new File(configPath); - myFilesToDelete.add(configFile); - - SvnVcs vcs = SvnVcs.getInstance(myProject); - - myAuthenticationManager = new SvnAuthenticationManager(myProject, configFile); - - myInteractiveProvider = new SvnTestInteractiveAuthentication(myAuthenticationManager); - myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, - SvnConfiguration.RUNTIME_AUTH_CACHE)); - myAuthenticationManager.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE); - - myTestInteraction = new TestInteraction(); - myAuthenticationManager.setInteraction(myTestInteraction); - - SVNConfigFile.createDefaultConfiguration(configFile); - } - - @Override - protected void tearDown() throws Exception { - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - try { - SvnAuthenticationTest.super.tearDown(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - }); - - FileUtil.delete(new File(myConfiguration.getConfigurationDirectory())); - } - - public void testSavedAndRead() throws Exception { - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final boolean[] result = new boolean[]{false}; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - if (SystemInfo.isWindows) { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.save)); - } else { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - } - - commonScheme(url, false, null); - Assert.assertEquals(3, listener.getCnt()); - //long start = System.currentTimeMillis(); - //waitListenerStep(start, listener, 3); - - listener.reset(); - if (!SystemInfo.isWindows) savedOnceListener.reset(); - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - if (! SystemInfo.isWindows) { - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - } - commonScheme(url, false, null); - //start = System.currentTimeMillis(); - //waitListenerStep(start, listener, 4); - Assert.assertEquals(SystemInfo.isWindows ? 1 : 3, listener.getCnt()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(SystemInfo.isWindows ? 0 : 2, myTestInteraction.getNumPlaintextPrompt()); - Assert.assertEquals(0, myTestInteraction.getNumAuthWarn()); - Assert.assertEquals(0, myTestInteraction.getNumPasswordsWarn()); - Assert.assertEquals(0, myTestInteraction.getNumSSLPlaintextPrompt()); - Assert.assertEquals(0, myTestInteraction.getNumSSLWarn()); - Assert.assertEquals(SystemInfo.isWindows ? 1 : 3, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - } - - public void testSavedAndReadUnix() throws Exception { - if (SystemInfo.isWindows) return; - - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - - final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); - final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); - FileUtil.copy(servers, oldServers); - try { - FileUtil.appendToFile(servers, "\nstore-plaintext-passwords=yes\n"); - - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.save)); - - commonScheme(url, false, null); - Assert.assertEquals(3, listener.getCnt()); - //long start = System.currentTimeMillis(); - //waitListenerStep(start, listener, 3); - - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - commonScheme(url, false, null); - //start = System.currentTimeMillis(); - //waitListenerStep(start, listener, 4); - Assert.assertEquals(4, listener.getCnt()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - } finally { - FileUtil.delete(servers); - FileUtil.rename(oldServers, servers); - } - - Assert.assertTrue(result[0]); - myTestInteraction.assertNothing(); - Assert.assertEquals(4, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - } - - private void waitListenerStep(long start, TestListener listener, final int stepNoNext) { - while (listener.getCnt() < stepNoNext && System.currentTimeMillis() - start < 10000) { - synchronized (mySynchObject) { - try { - mySynchObject.wait(50); - } - catch (InterruptedException e) { - // - } - } - } - Assert.assertEquals(stepNoNext, listener.getCnt()); - } - - public void testWhenNotSaved() throws Exception { - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - - myInteractiveProvider.setSaveData(false); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - - commonScheme(url, false, null); - //long start = System.currentTimeMillis(); - //waitListenerStep(start, listener, 2); - Assert.assertEquals(2, listener.getCnt()); - - savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); - // cause is not cleared though - savedOnceListener.reset(); - - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - commonScheme(url, false, null); - Assert.assertEquals(4, listener.getCnt()); - //start = System.currentTimeMillis(); - //waitListenerStep(start, listener, 4); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - myTestInteraction.assertNothing(); - Assert.assertEquals(4, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - } - - public void testWhenAuthCredsNoInConfig() throws Exception { - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - - final File config = new File(myConfiguration.getConfigurationDirectory(), "config"); - final String contents = FileUtil.loadFile(config); - final String auth = "[auth]"; - final int idx = contents.indexOf(auth); - Assert.assertTrue(idx != -1); - final String newContents = contents.substring(0, idx + auth.length()) + "\nstore-auth-creds=no\n" + contents.substring(idx + auth.length()); - - final File oldConfig = new File(myConfiguration.getConfigurationDirectory(), "config_old"); - FileUtil.rename(config, oldConfig); - try { - config.createNewFile(); - FileUtil.appendToFile(config, newContents); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - - commonScheme(url, false, null); - Assert.assertEquals(2, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); - myTestInteraction.reset(); - savedOnceListener.assertForAwt(); - savedOnceListener.reset(); - - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - commonScheme(url, false, null); - Assert.assertEquals(4, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); - Assert.assertEquals(4, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - } finally { - FileUtil.delete(config); - FileUtil.rename(oldConfig, config); - } - } - - public void testWhenAuthCredsNoInServers() throws Exception { - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - - final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); - - final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); - FileUtil.copy(servers, oldServers); - try { - FileUtil.appendToFile(servers, "\nstore-auth-creds=no\n"); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - - commonScheme(url, false, null); - Assert.assertEquals(2, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); - myTestInteraction.reset(); - savedOnceListener.assertForAwt(); - savedOnceListener.reset(); - - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - commonScheme(url, false, null); - Assert.assertEquals(4, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); - Assert.assertEquals(4, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - } finally { - FileUtil.delete(servers); - FileUtil.rename(oldServers, servers); - } - } - - public void testWhenPassSaveNoInConfig() throws Exception { - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - - final File config = new File(myConfiguration.getConfigurationDirectory(), "config"); - final String contents = FileUtil.loadFile(config); - final String auth = "[auth]"; - final int idx = contents.indexOf(auth); - Assert.assertTrue(idx != -1); - final String newContents = contents.substring(0, idx + auth.length()) + "\nstore-passwords=no\n" + contents.substring(idx + auth.length()); - - final File oldConfig = new File(myConfiguration.getConfigurationDirectory(), "config_old"); - FileUtil.rename(config, oldConfig); - try { - config.createNewFile(); - FileUtil.appendToFile(config, newContents); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - - commonScheme(url, false, null); - Assert.assertEquals(3, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); - myTestInteraction.reset(); - savedOnceListener.assertForAwt(); - savedOnceListener.reset(); - - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - commonScheme(url, false, null); - Assert.assertEquals(6, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); - Assert.assertEquals(6, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - } finally { - FileUtil.delete(config); - FileUtil.rename(oldConfig, config); - } - } - - public void testWhenPassSaveNoInServers() throws Exception { - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - - final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); - - final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); - FileUtil.copy(servers, oldServers); - try { - FileUtil.appendToFile(servers, "\nstore-passwords=no\n"); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - - commonScheme(url, false, null); - Assert.assertEquals(3, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); - myTestInteraction.reset(); - savedOnceListener.assertForAwt(); - savedOnceListener.reset(); - - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - commonScheme(url, false, null); - Assert.assertEquals(6, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); - Assert.assertEquals(6, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - } finally { - FileUtil.delete(servers); - FileUtil.rename(oldServers, servers); - } - } - - public void testWhenPassSaveNoForGroup() throws Exception { - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - - final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); - final String contents = FileUtil.loadFile(servers); - final String groups = "[groups]"; - final int idx = contents.indexOf(groups); - Assert.assertTrue(idx != -1); - final String newContents = contents.substring(0, idx + groups.length()) + "\nsomegroup=some*\n" + contents.substring(idx + groups.length()) + - "\n[somegroup]\nstore-passwords=no\n"; - - final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); - FileUtil.rename(servers, oldServers); - try { - servers.createNewFile(); - FileUtil.appendToFile(servers, newContents); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - - commonScheme(url, false, null); - Assert.assertEquals(3, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); - myTestInteraction.reset(); - savedOnceListener.assertForAwt(); - savedOnceListener.reset(); - - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - commonScheme(url, false, null); - Assert.assertEquals(6, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); - Assert.assertEquals(6, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - } finally { - FileUtil.delete(servers); - FileUtil.rename(oldServers, servers); - } - } - - public void testWhenPassPhraseSaveNo() throws Exception { - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - - final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); - - final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); - FileUtil.copy(servers, oldServers); - try { - FileUtil.appendToFile(servers, "\nstore-ssl-client-cert-pp=no\n"); - - final SVNURL url = SVNURL.parseURIEncoded("https://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - - commonScheme(url, false, null); - Assert.assertEquals(3, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumSSLWarn()); - myTestInteraction.reset(); - savedOnceListener.assertForAwt(); - savedOnceListener.reset(); - - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - commonScheme(url, false, null); - Assert.assertEquals(6, listener.getCnt()); - Assert.assertEquals(1, myTestInteraction.getNumSSLWarn()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(1, myTestInteraction.getNumSSLWarn()); - Assert.assertEquals(6, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.SSL); - - if (exception[0] != null) { - throw exception[0]; - } - } finally { - FileUtil.delete(servers); - FileUtil.rename(oldServers, servers); - } - } - - public void testPlaintextPrompt() throws Exception { - SVNJNAUtil.setJNAEnabled(false); - - // yes, no - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - myTestInteraction.setPlaintextAnswer(true); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.save)); - - commonScheme(url, false, null); - long start = System.currentTimeMillis(); - waitListenerStep(start, listener, 3); - Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); - savedOnceListener.reset(); - myTestInteraction.reset(); - - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - try { - myConfiguration.clearAuthenticationDirectory(getProject()); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - }); - - myTestInteraction.setPlaintextAnswer(false); - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - commonScheme(url, false, null); - start = System.currentTimeMillis(); - waitListenerStep(start, listener, 6); - Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); - - savedOnceListener.reset(); - myTestInteraction.reset(); - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - commonScheme(url, false, null); - start = System.currentTimeMillis(); - waitListenerStep(start, listener, 9); - Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); - Assert.assertEquals(9, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - SVNJNAUtil.setJNAEnabled(true); - } - - public void testPlaintextPromptAndSecondPrompt() throws Exception { - SVNJNAUtil.setJNAEnabled(false); - - // yes, no - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - myTestInteraction.setPlaintextAnswer(false); - - final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); - final SVNURL url2 = SVNURL.parseURIEncoded("http://some.other.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - - commonScheme(url, false, null); - long start = System.currentTimeMillis(); - waitListenerStep(start, listener, 3); - Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); - // actually password not saved, but save was called - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); - savedOnceListener.reset(); - myTestInteraction.reset(); - - listener.addStep(new Trinity(ProviderType.persistent, url2, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url2, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url2, Type.without_pasword_save)); - commonScheme(url2, false, "anotherRealm"); - start = System.currentTimeMillis(); - waitListenerStep(start, listener, 6); - Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); - Assert.assertEquals(6, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - // didn't called to save for 2nd time - savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); - - if (exception[0] != null) { - throw exception[0]; - } - SVNJNAUtil.setJNAEnabled(true); - } - - public void testPlaintextSSLPrompt() throws Exception { - SVNJNAUtil.setJNAEnabled(false); - - // yes, no - final TestListener listener = new TestListener(mySynchObject); - myAuthenticationManager.addListener(listener); - final SavedOnceListener savedOnceListener = new SavedOnceListener(); - myAuthenticationManager.addListener(savedOnceListener); - myTestInteraction.setSSLPlaintextAnswer(true); - - final SVNURL url = SVNURL.parseURIEncoded("https://some.host.com/repo"); - - final SVNException[] exception = new SVNException[1]; - final Boolean[] result = new Boolean[1]; - synchronousBackground(new Runnable() { - @Override - public void run() { - try { - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.save)); - - commonScheme(url, false, null); - long start = System.currentTimeMillis(); - waitListenerStep(start, listener, 3); - Assert.assertEquals(1, myTestInteraction.getNumSSLPlaintextPrompt()); - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.SSL); - savedOnceListener.reset(); - myTestInteraction.reset(); - - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - try { - myConfiguration.clearAuthenticationDirectory(getProject()); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - }); - - myTestInteraction.setSSLPlaintextAnswer(false); - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - commonScheme(url, false, null); - start = System.currentTimeMillis(); - waitListenerStep(start, listener, 6); - Assert.assertEquals(1, myTestInteraction.getNumSSLPlaintextPrompt()); - - SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); - myTestInteraction.reset(); - savedOnceListener.reset(); - - listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); - listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); - listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); - commonScheme(url, false, null); - start = System.currentTimeMillis(); - waitListenerStep(start, listener, 9); - Assert.assertEquals(1, myTestInteraction.getNumSSLPlaintextPrompt()); - } - catch (SVNException e) { - exception[0] = e; - } - result[0] = true; - } - }); - - Assert.assertTrue(result[0]); - Assert.assertEquals(1, myTestInteraction.getNumSSLPlaintextPrompt()); - Assert.assertEquals(9, listener.getCnt()); - listener.assertForAwt(); - savedOnceListener.assertForAwt(); - savedOnceListener.assertSaved(url, ISVNAuthenticationManager.SSL); - - if (exception[0] != null) { - throw exception[0]; - } - SVNJNAUtil.setJNAEnabled(true); - } - - private static void synchronousBackground(final Runnable runnable) throws InterruptedException { - final Semaphore semaphore = new Semaphore(); - semaphore.down(); - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - try { - runnable.run(); - } finally { - semaphore.up(); - } - } - }); - semaphore.waitFor(); - } - - private void commonScheme(final SVNURL url, final boolean username, final String realm) throws SVNException { - String kind = null; - - final String actualRealm = realm == null ? "realm" : realm; - final String protocol = url.getProtocol(); - if (username) { - kind = ISVNAuthenticationManager.USERNAME; - } else if ("svn+ssh".equals(protocol)) { - kind = ISVNAuthenticationManager.SSH; - } else if ("http".equals(protocol)) { - kind = ISVNAuthenticationManager.PASSWORD; - } else if ("https".equals(protocol)) { - kind = ISVNAuthenticationManager.SSL; - } else if ("file".equals(protocol)) { - kind = ISVNAuthenticationManager.USERNAME; - } - SVNAuthentication authentication = null; - try { - authentication = myAuthenticationManager.getFirstAuthentication(kind, actualRealm, url); - while (! passwordSpecified(authentication)) { - authentication = myAuthenticationManager.getNextAuthentication(kind, actualRealm, url); - } - } finally { - myAuthenticationManager.acknowledgeAuthentication(authentication != null, kind, actualRealm, null, authentication); - } - } - - private static class SvnTestInteractiveAuthentication implements ISVNAuthenticationProvider { - private final SvnAuthenticationManager myManager; - private boolean mySaveData; - - public SvnTestInteractiveAuthentication(SvnAuthenticationManager manager) { - myManager = manager; - mySaveData = true; - } - - public void setSaveData(boolean saveData) { - mySaveData = saveData; - } - - @Override - public int acceptServerAuthentication(SVNURL url, String realm, Object certificate, boolean resultMayBeStored) { - return ISVNAuthenticationProvider.REJECTED; - } - - @Override - public SVNAuthentication requestClientAuthentication(String kind, - SVNURL url, - String realm, - SVNErrorMessage errorMessage, - SVNAuthentication previousAuth, - boolean authMayBeStored) { - authMayBeStored = authMayBeStored && mySaveData; - SVNAuthentication result = null; - if (ISVNAuthenticationManager.USERNAME.equals(kind)) { - result = new SVNUserNameAuthentication("username", authMayBeStored); - } else if (ISVNAuthenticationManager.PASSWORD.equals(kind)) { - result = new SVNPasswordAuthentication("username", "abc", authMayBeStored, url, false); - } else if (ISVNAuthenticationManager.SSH.equals(kind)) { - result = new SVNSSHAuthentication("username", "abc", -1, authMayBeStored, url, false); - } else if (ISVNAuthenticationManager.SSL.equals(kind)) { - result = new SVNSSLAuthentication(new File("aaa"), "abc", authMayBeStored, url, false); - } - if (! ISVNAuthenticationManager.USERNAME.equals(kind)) { - myManager.requested(ProviderType.interactive, url, realm, kind, result == null); - } - return result; - } - } - - private static boolean passwordSpecified(final SVNAuthentication authentication) { - final String kind = authentication.getKind(); - if (ISVNAuthenticationManager.SSH.equals(kind)) { - if (((SVNSSHAuthentication) authentication).hasPrivateKey()) { - return ((SVNSSHAuthentication) authentication).getPassphrase() != null && - (((SVNSSHAuthentication) authentication).getPrivateKey() != null || - ((SVNSSHAuthentication) authentication).getPrivateKeyFile() != null); - } else { - return ((SVNSSHAuthentication) authentication).getPassword() != null; - } - } else if (ISVNAuthenticationManager.PASSWORD.equals(kind)) { - return ((SVNPasswordAuthentication) authentication).getPassword() != null; - } else if (ISVNAuthenticationManager.SSL.equals(kind)) { - return ((SVNSSLAuthentication) authentication).getPassword() != null; - } else if (ISVNAuthenticationManager.USERNAME.equals(kind)) { - return true; - } - return true; - } - - private static class TestListener implements SvnAuthenticationListener { - private final List> myExpectedSequence; - private int myCnt; - private final Object mySynchObject; - private boolean mySuccess; - private boolean mySaved; - - private TestListener(final Object synchObject) { - mySynchObject = synchObject; - myExpectedSequence = new ArrayList>(); - myCnt = 0; - mySuccess = true; - } - - public void addStep(Trinity step) { - myExpectedSequence.add(step); - } - - public int getCnt() { - return myCnt; - } - - @Override - public void saveAttemptStarted(ProviderType type, SVNURL url, String realm, String kind) { - mySaved = false; - } - - @Override - public void saveAttemptFinished(ProviderType type, SVNURL url, String realm, String kind) { - if (! mySuccess) return; - if (! mySaved) { - saveRegistration(type, url, false); - } - } - - @Override - public void acknowledge(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication authentication) { - } - - @Override - public void actualSaveWillBeTried(ProviderType type, SVNURL url, String realm, String kind) { - if (! mySuccess) return; - mySaved = true; - saveRegistration(type, url, true); - } - - private void saveRegistration(ProviderType type, SVNURL url, boolean withCredentials) { - mySuccess = myExpectedSequence.get(myCnt).equals(new Trinity(type, url, withCredentials ? Type.save : Type.without_pasword_save)); - if (mySuccess) { - ++ myCnt; - } - synchronized (mySynchObject) { - mySynchObject.notifyAll(); - } - } - - public void reset() { - myExpectedSequence.clear(); - myCnt = 0; - mySuccess = true; - } - - @Override - public void requested(ProviderType type, SVNURL url, String realm, String kind, boolean canceled) { - if (! mySuccess) return; - - mySuccess = myExpectedSequence.get(myCnt).equals(new Trinity(type, url, Type.request)); - if (mySuccess) { - ++ myCnt; - } - synchronized (mySynchObject) { - mySynchObject.notifyAll(); - } - } - - public void assertForAwt() { - Assert.assertTrue("last cnt = " + myCnt, mySuccess); - } - } - - private static class SavedOnceListener implements SvnAuthenticationListener { - private final Set> myClientRequested; - private final Set> mySaved; - private String myCause; - private boolean mySaveCalled; - - private SavedOnceListener() { - myClientRequested = new HashSet>(); - mySaved = new HashSet>(); - } - - public void reset() { - mySaved.clear(); - myClientRequested.clear(); - } - - @Override - public void saveAttemptStarted(ProviderType type, SVNURL url, String realm, String kind) { - mySaveCalled = false; - } - - @Override - public void saveAttemptFinished(ProviderType type, SVNURL url, String realm, String kind) { - if (! mySaveCalled) { - saveRegistration(url, kind); - } - } - - @Override - public void acknowledge(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication authentication) { - } - - @Override - public void actualSaveWillBeTried(ProviderType type, SVNURL url, String realm, String kind) { - mySaveCalled = true; - saveRegistration(url, kind); - } - - private void saveRegistration(SVNURL url, String kind) { - final Pair pair = new Pair(url, kind); - if (mySaved.contains(pair)) { - myCause = "saved twice"; - } - mySaved.add(pair); - } - - public boolean isSaved(final SVNURL url, final String kind) { - return mySaved.contains(new Pair(url, kind)); - } - - @Override - public void requested(ProviderType type, SVNURL url, String realm, String kind, boolean canceled) { - if (ProviderType.interactive.equals(type)) { - final Pair pair = new Pair(url, kind); - if (myClientRequested.contains(pair)) { - myCause = "client requested twice"; - } - myClientRequested.add(pair); - } - } - - public void assertForAwt() { - assertNull(myCause, myCause); - } - - public void assertSaved(final SVNURL url, final String kind) { - Assert.assertTrue("not saved", mySaved.contains(new Pair(url, kind))); - } - - public void assertNotSaved(final SVNURL url, final String kind) { - Assert.assertTrue("saved", ! mySaved.contains(new Pair(url, kind))); - } - } - - private static enum Type { - request, - save, - without_pasword_save - } - - private static class TestInteraction implements SvnAuthenticationInteraction { - private int myNumAuthWarn; - private int myNumPasswordsWarn; - private int myNumSSLWarn; - private int myNumPlaintextPrompt; - private int myNumSSLPlaintextPrompt; - - private boolean myPlaintextAnswer; - private boolean mySSLPlaintextAnswer; - - public void setPlaintextAnswer(boolean plaintextAnswer) { - myPlaintextAnswer = plaintextAnswer; - } - - public void setSSLPlaintextAnswer(boolean SSLPlaintextAnswer) { - mySSLPlaintextAnswer = SSLPlaintextAnswer; - } - - public void assertNothing() { - Assert.assertEquals("myNumAuthWarn", myNumAuthWarn, 0); - Assert.assertEquals("myNumPasswordsWarn", myNumPasswordsWarn, 0); - Assert.assertEquals("myNumSSLWarn", myNumSSLWarn, 0); - Assert.assertEquals("myNumPlaintextPrompt", myNumPlaintextPrompt, 0); - Assert.assertEquals("myNumSSLPlaintextPrompt", myNumSSLPlaintextPrompt, 0); - } - - public void reset() { - myNumAuthWarn = 0; - myNumPasswordsWarn = 0; - myNumSSLWarn = 0; - myNumPlaintextPrompt = 0; - myNumSSLPlaintextPrompt = 0; - } - - @Override - public boolean promptForPlaintextPasswordSaving(SVNURL url, String realm) { - ++ myNumPlaintextPrompt; - return myPlaintextAnswer; - } - - @Override - public boolean promptInAwt() { - return false; - } - - @Override - public void warnOnAuthStorageDisabled(SVNURL url) { - ++ myNumAuthWarn; - } - - @Override - public void warnOnPasswordStorageDisabled(SVNURL url) { - ++ myNumPasswordsWarn; - } - - @Override - public void warnOnSSLPassphraseStorageDisabled(SVNURL url) { - ++ myNumSSLWarn; - } - - @Override - public boolean promptForSSLPlaintextPassphraseSaving(SVNURL url, String realm, File certificateFile, String certificateName) { - ++ myNumSSLPlaintextPrompt; - return mySSLPlaintextAnswer; - } - - public int getNumAuthWarn() { - return myNumAuthWarn; - } - - public int getNumPasswordsWarn() { - return myNumPasswordsWarn; - } - - public int getNumPlaintextPrompt() { - return myNumPlaintextPrompt; - } - - public int getNumSSLPlaintextPrompt() { - return myNumSSLPlaintextPrompt; - } - - public int getNumSSLWarn() { - return myNumSSLWarn; - } - } + //TODO[kb] uncomment + //private SvnAuthenticationManager myAuthenticationManager; + //private TestInteraction myTestInteraction; + //private final Object mySynchObject = new Object(); + //private SvnTestInteractiveAuthentication myInteractiveProvider; + //private SvnConfiguration myConfiguration; + // + //@Override + //protected void runBareRunnable(Runnable runnable) throws Throwable { + // runnable.run(); + //} + // + //@Override + //protected void setUp() throws Exception { + // UIUtil.invokeAndWaitIfNeeded(new Runnable() { + // @Override + // public void run() { + // try { + // SvnAuthenticationTest.super.setUp(); + // } + // catch (Exception e) { + // throw new RuntimeException(e); + // } + // } + // }); + // + // + // myConfiguration = SvnConfiguration.getInstance(myProject); + // final String configPath = myProject.getBaseDir().getPath() + File.separator + "Subversion"; + // myConfiguration.setConfigurationDirectory(configPath); + // + // final File configFile = new File(configPath); + // myFilesToDelete.add(configFile); + // + // SvnVcs vcs = SvnVcs.getInstance(myProject); + // + // myAuthenticationManager = new SvnAuthenticationManager(myProject, configFile); + // + // myInteractiveProvider = new SvnTestInteractiveAuthentication(myAuthenticationManager); + // myAuthenticationManager.setAuthenticationProvider(new SvnAuthenticationProvider(vcs, myInteractiveProvider, + // SvnConfiguration.RUNTIME_AUTH_CACHE)); + // myAuthenticationManager.setRuntimeStorage(SvnConfiguration.RUNTIME_AUTH_CACHE); + // + // myTestInteraction = new TestInteraction(); + // myAuthenticationManager.setInteraction(myTestInteraction); + // + // SVNConfigFile.createDefaultConfiguration(configFile); + //} + // + //@Override + //protected void tearDown() throws Exception { + // UIUtil.invokeAndWaitIfNeeded(new Runnable() { + // @Override + // public void run() { + // try { + // SvnAuthenticationTest.super.tearDown(); + // } + // catch (Exception e) { + // throw new RuntimeException(e); + // } + // } + // }); + // + // FileUtil.delete(new File(myConfiguration.getConfigurationDirectory())); + //} + // + //public void testSavedAndRead() throws Exception { + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final boolean[] result = new boolean[]{false}; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // if (SystemInfo.isWindows) { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.save)); + // } else { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // } + // + // commonScheme(url, false, null); + // Assert.assertEquals(3, listener.getCnt()); + // //long start = System.currentTimeMillis(); + // //waitListenerStep(start, listener, 3); + // + // listener.reset(); + // if (!SystemInfo.isWindows) savedOnceListener.reset(); + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // if (! SystemInfo.isWindows) { + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // } + // commonScheme(url, false, null); + // //start = System.currentTimeMillis(); + // //waitListenerStep(start, listener, 4); + // Assert.assertEquals(SystemInfo.isWindows ? 1 : 3, listener.getCnt()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(SystemInfo.isWindows ? 0 : 2, myTestInteraction.getNumPlaintextPrompt()); + // Assert.assertEquals(0, myTestInteraction.getNumAuthWarn()); + // Assert.assertEquals(0, myTestInteraction.getNumPasswordsWarn()); + // Assert.assertEquals(0, myTestInteraction.getNumSSLPlaintextPrompt()); + // Assert.assertEquals(0, myTestInteraction.getNumSSLWarn()); + // Assert.assertEquals(SystemInfo.isWindows ? 1 : 3, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + //} + // + //public void testSavedAndReadUnix() throws Exception { + // if (SystemInfo.isWindows) return; + // + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // + // final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); + // final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); + // FileUtil.copy(servers, oldServers); + // try { + // FileUtil.appendToFile(servers, "\nstore-plaintext-passwords=yes\n"); + // + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.save)); + // + // commonScheme(url, false, null); + // Assert.assertEquals(3, listener.getCnt()); + // //long start = System.currentTimeMillis(); + // //waitListenerStep(start, listener, 3); + // + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // commonScheme(url, false, null); + // //start = System.currentTimeMillis(); + // //waitListenerStep(start, listener, 4); + // Assert.assertEquals(4, listener.getCnt()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // } finally { + // FileUtil.delete(servers); + // FileUtil.rename(oldServers, servers); + // } + // + // Assert.assertTrue(result[0]); + // myTestInteraction.assertNothing(); + // Assert.assertEquals(4, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + //} + // + //private void waitListenerStep(long start, TestListener listener, final int stepNoNext) { + // while (listener.getCnt() < stepNoNext && System.currentTimeMillis() - start < 10000) { + // synchronized (mySynchObject) { + // try { + // mySynchObject.wait(50); + // } + // catch (InterruptedException e) { + // // + // } + // } + // } + // Assert.assertEquals(stepNoNext, listener.getCnt()); + //} + // + //public void testWhenNotSaved() throws Exception { + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // + // myInteractiveProvider.setSaveData(false); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // + // commonScheme(url, false, null); + // //long start = System.currentTimeMillis(); + // //waitListenerStep(start, listener, 2); + // Assert.assertEquals(2, listener.getCnt()); + // + // savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); + // // cause is not cleared though + // savedOnceListener.reset(); + // + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // commonScheme(url, false, null); + // Assert.assertEquals(4, listener.getCnt()); + // //start = System.currentTimeMillis(); + // //waitListenerStep(start, listener, 4); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // myTestInteraction.assertNothing(); + // Assert.assertEquals(4, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + //} + // + //public void testWhenAuthCredsNoInConfig() throws Exception { + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // + // final File config = new File(myConfiguration.getConfigurationDirectory(), "config"); + // final String contents = FileUtil.loadFile(config); + // final String auth = "[auth]"; + // final int idx = contents.indexOf(auth); + // Assert.assertTrue(idx != -1); + // final String newContents = contents.substring(0, idx + auth.length()) + "\nstore-auth-creds=no\n" + contents.substring(idx + auth.length()); + // + // final File oldConfig = new File(myConfiguration.getConfigurationDirectory(), "config_old"); + // FileUtil.rename(config, oldConfig); + // try { + // config.createNewFile(); + // FileUtil.appendToFile(config, newContents); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // + // commonScheme(url, false, null); + // Assert.assertEquals(2, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); + // myTestInteraction.reset(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.reset(); + // + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // commonScheme(url, false, null); + // Assert.assertEquals(4, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); + // Assert.assertEquals(4, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + // } finally { + // FileUtil.delete(config); + // FileUtil.rename(oldConfig, config); + // } + //} + // + //public void testWhenAuthCredsNoInServers() throws Exception { + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // + // final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); + // + // final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); + // FileUtil.copy(servers, oldServers); + // try { + // FileUtil.appendToFile(servers, "\nstore-auth-creds=no\n"); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // + // commonScheme(url, false, null); + // Assert.assertEquals(2, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); + // myTestInteraction.reset(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.reset(); + // + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // commonScheme(url, false, null); + // Assert.assertEquals(4, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(1, myTestInteraction.getNumAuthWarn()); + // Assert.assertEquals(4, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + // } finally { + // FileUtil.delete(servers); + // FileUtil.rename(oldServers, servers); + // } + //} + // + //public void testWhenPassSaveNoInConfig() throws Exception { + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // + // final File config = new File(myConfiguration.getConfigurationDirectory(), "config"); + // final String contents = FileUtil.loadFile(config); + // final String auth = "[auth]"; + // final int idx = contents.indexOf(auth); + // Assert.assertTrue(idx != -1); + // final String newContents = contents.substring(0, idx + auth.length()) + "\nstore-passwords=no\n" + contents.substring(idx + auth.length()); + // + // final File oldConfig = new File(myConfiguration.getConfigurationDirectory(), "config_old"); + // FileUtil.rename(config, oldConfig); + // try { + // config.createNewFile(); + // FileUtil.appendToFile(config, newContents); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // + // commonScheme(url, false, null); + // Assert.assertEquals(3, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); + // myTestInteraction.reset(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.reset(); + // + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // commonScheme(url, false, null); + // Assert.assertEquals(6, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); + // Assert.assertEquals(6, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + // } finally { + // FileUtil.delete(config); + // FileUtil.rename(oldConfig, config); + // } + //} + // + //public void testWhenPassSaveNoInServers() throws Exception { + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // + // final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); + // + // final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); + // FileUtil.copy(servers, oldServers); + // try { + // FileUtil.appendToFile(servers, "\nstore-passwords=no\n"); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // + // commonScheme(url, false, null); + // Assert.assertEquals(3, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); + // myTestInteraction.reset(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.reset(); + // + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // commonScheme(url, false, null); + // Assert.assertEquals(6, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); + // Assert.assertEquals(6, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + // } finally { + // FileUtil.delete(servers); + // FileUtil.rename(oldServers, servers); + // } + //} + // + //public void testWhenPassSaveNoForGroup() throws Exception { + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // + // final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); + // final String contents = FileUtil.loadFile(servers); + // final String groups = "[groups]"; + // final int idx = contents.indexOf(groups); + // Assert.assertTrue(idx != -1); + // final String newContents = contents.substring(0, idx + groups.length()) + "\nsomegroup=some*\n" + contents.substring(idx + groups.length()) + + // "\n[somegroup]\nstore-passwords=no\n"; + // + // final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); + // FileUtil.rename(servers, oldServers); + // try { + // servers.createNewFile(); + // FileUtil.appendToFile(servers, newContents); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // + // commonScheme(url, false, null); + // Assert.assertEquals(3, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); + // myTestInteraction.reset(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.reset(); + // + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // commonScheme(url, false, null); + // Assert.assertEquals(6, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(1, myTestInteraction.getNumPasswordsWarn()); + // Assert.assertEquals(6, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + // } finally { + // FileUtil.delete(servers); + // FileUtil.rename(oldServers, servers); + // } + //} + // + //public void testWhenPassPhraseSaveNo() throws Exception { + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // + // final File servers = new File(myConfiguration.getConfigurationDirectory(), "servers"); + // + // final File oldServers = new File(myConfiguration.getConfigurationDirectory(), "config_old"); + // FileUtil.copy(servers, oldServers); + // try { + // FileUtil.appendToFile(servers, "\nstore-ssl-client-cert-pp=no\n"); + // + // final SVNURL url = SVNURL.parseURIEncoded("https://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // + // commonScheme(url, false, null); + // Assert.assertEquals(3, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumSSLWarn()); + // myTestInteraction.reset(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.reset(); + // + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // commonScheme(url, false, null); + // Assert.assertEquals(6, listener.getCnt()); + // Assert.assertEquals(1, myTestInteraction.getNumSSLWarn()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(1, myTestInteraction.getNumSSLWarn()); + // Assert.assertEquals(6, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.SSL); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + // } finally { + // FileUtil.delete(servers); + // FileUtil.rename(oldServers, servers); + // } + //} + // + //public void testPlaintextPrompt() throws Exception { + // SVNJNAUtil.setJNAEnabled(false); + // + // // yes, no + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // myTestInteraction.setPlaintextAnswer(true); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.save)); + // + // commonScheme(url, false, null); + // long start = System.currentTimeMillis(); + // waitListenerStep(start, listener, 3); + // Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); + // savedOnceListener.reset(); + // myTestInteraction.reset(); + // + // UIUtil.invokeAndWaitIfNeeded(new Runnable() { + // @Override + // public void run() { + // try { + // myConfiguration.clearAuthenticationDirectory(getProject()); + // } + // catch (Exception e) { + // throw new RuntimeException(e); + // } + // } + // }); + // + // myTestInteraction.setPlaintextAnswer(false); + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // commonScheme(url, false, null); + // start = System.currentTimeMillis(); + // waitListenerStep(start, listener, 6); + // Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); + // + // savedOnceListener.reset(); + // myTestInteraction.reset(); + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // commonScheme(url, false, null); + // start = System.currentTimeMillis(); + // waitListenerStep(start, listener, 9); + // Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); + // Assert.assertEquals(9, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + // SVNJNAUtil.setJNAEnabled(true); + //} + // + //public void testPlaintextPromptAndSecondPrompt() throws Exception { + // SVNJNAUtil.setJNAEnabled(false); + // + // // yes, no + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // myTestInteraction.setPlaintextAnswer(false); + // + // final SVNURL url = SVNURL.parseURIEncoded("http://some.host.com/repo"); + // final SVNURL url2 = SVNURL.parseURIEncoded("http://some.other.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // + // commonScheme(url, false, null); + // long start = System.currentTimeMillis(); + // waitListenerStep(start, listener, 3); + // Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); + // // actually password not saved, but save was called + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.PASSWORD); + // savedOnceListener.reset(); + // myTestInteraction.reset(); + // + // listener.addStep(new Trinity(ProviderType.persistent, url2, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url2, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url2, Type.without_pasword_save)); + // commonScheme(url2, false, "anotherRealm"); + // start = System.currentTimeMillis(); + // waitListenerStep(start, listener, 6); + // Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(1, myTestInteraction.getNumPlaintextPrompt()); + // Assert.assertEquals(6, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // // didn't called to save for 2nd time + // savedOnceListener.assertNotSaved(url, ISVNAuthenticationManager.PASSWORD); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + // SVNJNAUtil.setJNAEnabled(true); + //} + // + //public void testPlaintextSSLPrompt() throws Exception { + // SVNJNAUtil.setJNAEnabled(false); + // + // // yes, no + // final TestListener listener = new TestListener(mySynchObject); + // myAuthenticationManager.addListener(listener); + // final SavedOnceListener savedOnceListener = new SavedOnceListener(); + // myAuthenticationManager.addListener(savedOnceListener); + // myTestInteraction.setSSLPlaintextAnswer(true); + // + // final SVNURL url = SVNURL.parseURIEncoded("https://some.host.com/repo"); + // + // final SVNException[] exception = new SVNException[1]; + // final Boolean[] result = new Boolean[1]; + // synchronousBackground(new Runnable() { + // @Override + // public void run() { + // try { + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.save)); + // + // commonScheme(url, false, null); + // long start = System.currentTimeMillis(); + // waitListenerStep(start, listener, 3); + // Assert.assertEquals(1, myTestInteraction.getNumSSLPlaintextPrompt()); + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.SSL); + // savedOnceListener.reset(); + // myTestInteraction.reset(); + // + // UIUtil.invokeAndWaitIfNeeded(new Runnable() { + // @Override + // public void run() { + // try { + // myConfiguration.clearAuthenticationDirectory(getProject()); + // } + // catch (Exception e) { + // throw new RuntimeException(e); + // } + // } + // }); + // + // myTestInteraction.setSSLPlaintextAnswer(false); + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // commonScheme(url, false, null); + // start = System.currentTimeMillis(); + // waitListenerStep(start, listener, 6); + // Assert.assertEquals(1, myTestInteraction.getNumSSLPlaintextPrompt()); + // + // SvnConfiguration.RUNTIME_AUTH_CACHE.clear(); + // myTestInteraction.reset(); + // savedOnceListener.reset(); + // + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.interactive, url, Type.request)); + // listener.addStep(new Trinity(ProviderType.persistent, url, Type.without_pasword_save)); + // commonScheme(url, false, null); + // start = System.currentTimeMillis(); + // waitListenerStep(start, listener, 9); + // Assert.assertEquals(1, myTestInteraction.getNumSSLPlaintextPrompt()); + // } + // catch (SVNException e) { + // exception[0] = e; + // } + // result[0] = true; + // } + // }); + // + // Assert.assertTrue(result[0]); + // Assert.assertEquals(1, myTestInteraction.getNumSSLPlaintextPrompt()); + // Assert.assertEquals(9, listener.getCnt()); + // listener.assertForAwt(); + // savedOnceListener.assertForAwt(); + // savedOnceListener.assertSaved(url, ISVNAuthenticationManager.SSL); + // + // if (exception[0] != null) { + // throw exception[0]; + // } + // SVNJNAUtil.setJNAEnabled(true); + //} + // + //private static void synchronousBackground(final Runnable runnable) throws InterruptedException { + // final Semaphore semaphore = new Semaphore(); + // semaphore.down(); + // ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + // @Override + // public void run() { + // try { + // runnable.run(); + // } finally { + // semaphore.up(); + // } + // } + // }); + // semaphore.waitFor(); + //} + // + //private void commonScheme(final SVNURL url, final boolean username, final String realm) throws SVNException { + // String kind = null; + // + // final String actualRealm = realm == null ? "realm" : realm; + // final String protocol = url.getProtocol(); + // if (username) { + // kind = ISVNAuthenticationManager.USERNAME; + // } else if ("svn+ssh".equals(protocol)) { + // kind = ISVNAuthenticationManager.SSH; + // } else if ("http".equals(protocol)) { + // kind = ISVNAuthenticationManager.PASSWORD; + // } else if ("https".equals(protocol)) { + // kind = ISVNAuthenticationManager.SSL; + // } else if ("file".equals(protocol)) { + // kind = ISVNAuthenticationManager.USERNAME; + // } + // SVNAuthentication authentication = null; + // try { + // authentication = myAuthenticationManager.getFirstAuthentication(kind, actualRealm, url); + // while (! passwordSpecified(authentication)) { + // authentication = myAuthenticationManager.getNextAuthentication(kind, actualRealm, url); + // } + // } finally { + // myAuthenticationManager.acknowledgeAuthentication(authentication != null, kind, actualRealm, null, authentication); + // } + //} + // + //private static class SvnTestInteractiveAuthentication implements ISVNAuthenticationProvider { + // private final SvnAuthenticationManager myManager; + // private boolean mySaveData; + // + // public SvnTestInteractiveAuthentication(SvnAuthenticationManager manager) { + // myManager = manager; + // mySaveData = true; + // } + // + // public void setSaveData(boolean saveData) { + // mySaveData = saveData; + // } + // + // @Override + // public int acceptServerAuthentication(SVNURL url, String realm, Object certificate, boolean resultMayBeStored) { + // return ISVNAuthenticationProvider.REJECTED; + // } + // + // @Override + // public SVNAuthentication requestClientAuthentication(String kind, + // SVNURL url, + // String realm, + // SVNErrorMessage errorMessage, + // SVNAuthentication previousAuth, + // boolean authMayBeStored) { + // authMayBeStored = authMayBeStored && mySaveData; + // SVNAuthentication result = null; + // if (ISVNAuthenticationManager.USERNAME.equals(kind)) { + // result = new SVNUserNameAuthentication("username", authMayBeStored); + // } else if (ISVNAuthenticationManager.PASSWORD.equals(kind)) { + // result = new SVNPasswordAuthentication("username", "abc", authMayBeStored, url, false); + // } else if (ISVNAuthenticationManager.SSH.equals(kind)) { + // result = new SVNSSHAuthentication("username", "abc", -1, authMayBeStored, url, false); + // } else if (ISVNAuthenticationManager.SSL.equals(kind)) { + // result = new SVNSSLAuthentication(new File("aaa"), "abc", authMayBeStored, url, false); + // } + // if (! ISVNAuthenticationManager.USERNAME.equals(kind)) { + // myManager.requested(ProviderType.interactive, url, realm, kind, result == null); + // } + // return result; + // } + //} + // + //private static boolean passwordSpecified(final SVNAuthentication authentication) { + // final String kind = authentication.getKind(); + // if (ISVNAuthenticationManager.SSH.equals(kind)) { + // if (((SVNSSHAuthentication) authentication).hasPrivateKey()) { + // return ((SVNSSHAuthentication) authentication).getPassphrase() != null && + // (((SVNSSHAuthentication) authentication).getPrivateKey() != null || + // ((SVNSSHAuthentication) authentication).getPrivateKeyFile() != null); + // } else { + // return ((SVNSSHAuthentication) authentication).getPassword() != null; + // } + // } else if (ISVNAuthenticationManager.PASSWORD.equals(kind)) { + // return ((SVNPasswordAuthentication) authentication).getPassword() != null; + // } else if (ISVNAuthenticationManager.SSL.equals(kind)) { + // return ((SVNSSLAuthentication) authentication).getPassword() != null; + // } else if (ISVNAuthenticationManager.USERNAME.equals(kind)) { + // return true; + // } + // return true; + //} + // + //private static class TestListener implements SvnAuthenticationListener { + // private final List> myExpectedSequence; + // private int myCnt; + // private final Object mySynchObject; + // private boolean mySuccess; + // private boolean mySaved; + // + // private TestListener(final Object synchObject) { + // mySynchObject = synchObject; + // myExpectedSequence = new ArrayList>(); + // myCnt = 0; + // mySuccess = true; + // } + // + // public void addStep(Trinity step) { + // myExpectedSequence.add(step); + // } + // + // public int getCnt() { + // return myCnt; + // } + // + // @Override + // public void saveAttemptStarted(ProviderType type, SVNURL url, String realm, String kind) { + // mySaved = false; + // } + // + // @Override + // public void saveAttemptFinished(ProviderType type, SVNURL url, String realm, String kind) { + // if (! mySuccess) return; + // if (! mySaved) { + // saveRegistration(type, url, false); + // } + // } + // + // @Override + // public void acknowledge(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication authentication) { + // } + // + // @Override + // public void actualSaveWillBeTried(ProviderType type, SVNURL url, String realm, String kind) { + // if (! mySuccess) return; + // mySaved = true; + // saveRegistration(type, url, true); + // } + // + // private void saveRegistration(ProviderType type, SVNURL url, boolean withCredentials) { + // mySuccess = myExpectedSequence.get(myCnt).equals(new Trinity(type, url, withCredentials ? Type.save : Type.without_pasword_save)); + // if (mySuccess) { + // ++ myCnt; + // } + // synchronized (mySynchObject) { + // mySynchObject.notifyAll(); + // } + // } + // + // public void reset() { + // myExpectedSequence.clear(); + // myCnt = 0; + // mySuccess = true; + // } + // + // @Override + // public void requested(ProviderType type, SVNURL url, String realm, String kind, boolean canceled) { + // if (! mySuccess) return; + // + // mySuccess = myExpectedSequence.get(myCnt).equals(new Trinity(type, url, Type.request)); + // if (mySuccess) { + // ++ myCnt; + // } + // synchronized (mySynchObject) { + // mySynchObject.notifyAll(); + // } + // } + // + // public void assertForAwt() { + // Assert.assertTrue("last cnt = " + myCnt, mySuccess); + // } + //} + // + //private static class SavedOnceListener implements SvnAuthenticationListener { + // private final Set> myClientRequested; + // private final Set> mySaved; + // private String myCause; + // private boolean mySaveCalled; + // + // private SavedOnceListener() { + // myClientRequested = new HashSet>(); + // mySaved = new HashSet>(); + // } + // + // public void reset() { + // mySaved.clear(); + // myClientRequested.clear(); + // } + // + // @Override + // public void saveAttemptStarted(ProviderType type, SVNURL url, String realm, String kind) { + // mySaveCalled = false; + // } + // + // @Override + // public void saveAttemptFinished(ProviderType type, SVNURL url, String realm, String kind) { + // if (! mySaveCalled) { + // saveRegistration(url, kind); + // } + // } + // + // @Override + // public void acknowledge(boolean accepted, String kind, String realm, SVNErrorMessage message, SVNAuthentication authentication) { + // } + // + // @Override + // public void actualSaveWillBeTried(ProviderType type, SVNURL url, String realm, String kind) { + // mySaveCalled = true; + // saveRegistration(url, kind); + // } + // + // private void saveRegistration(SVNURL url, String kind) { + // final Pair pair = new Pair(url, kind); + // if (mySaved.contains(pair)) { + // myCause = "saved twice"; + // } + // mySaved.add(pair); + // } + // + // public boolean isSaved(final SVNURL url, final String kind) { + // return mySaved.contains(new Pair(url, kind)); + // } + // + // @Override + // public void requested(ProviderType type, SVNURL url, String realm, String kind, boolean canceled) { + // if (ProviderType.interactive.equals(type)) { + // final Pair pair = new Pair(url, kind); + // if (myClientRequested.contains(pair)) { + // myCause = "client requested twice"; + // } + // myClientRequested.add(pair); + // } + // } + // + // public void assertForAwt() { + // assertNull(myCause, myCause); + // } + // + // public void assertSaved(final SVNURL url, final String kind) { + // Assert.assertTrue("not saved", mySaved.contains(new Pair(url, kind))); + // } + // + // public void assertNotSaved(final SVNURL url, final String kind) { + // Assert.assertTrue("saved", ! mySaved.contains(new Pair(url, kind))); + // } + //} + // + //private static enum Type { + // request, + // save, + // without_pasword_save + //} + // + //private static class TestInteraction implements SvnAuthenticationInteraction { + // private int myNumAuthWarn; + // private int myNumPasswordsWarn; + // private int myNumSSLWarn; + // private int myNumPlaintextPrompt; + // private int myNumSSLPlaintextPrompt; + // + // private boolean myPlaintextAnswer; + // private boolean mySSLPlaintextAnswer; + // + // public void setPlaintextAnswer(boolean plaintextAnswer) { + // myPlaintextAnswer = plaintextAnswer; + // } + // + // public void setSSLPlaintextAnswer(boolean SSLPlaintextAnswer) { + // mySSLPlaintextAnswer = SSLPlaintextAnswer; + // } + // + // public void assertNothing() { + // Assert.assertEquals("myNumAuthWarn", myNumAuthWarn, 0); + // Assert.assertEquals("myNumPasswordsWarn", myNumPasswordsWarn, 0); + // Assert.assertEquals("myNumSSLWarn", myNumSSLWarn, 0); + // Assert.assertEquals("myNumPlaintextPrompt", myNumPlaintextPrompt, 0); + // Assert.assertEquals("myNumSSLPlaintextPrompt", myNumSSLPlaintextPrompt, 0); + // } + // + // public void reset() { + // myNumAuthWarn = 0; + // myNumPasswordsWarn = 0; + // myNumSSLWarn = 0; + // myNumPlaintextPrompt = 0; + // myNumSSLPlaintextPrompt = 0; + // } + // + // @Override + // public boolean promptForPlaintextPasswordSaving(SVNURL url, String realm) { + // ++ myNumPlaintextPrompt; + // return myPlaintextAnswer; + // } + // + // @Override + // public boolean promptInAwt() { + // return false; + // } + // + // @Override + // public void warnOnAuthStorageDisabled(SVNURL url) { + // ++ myNumAuthWarn; + // } + // + // @Override + // public void warnOnPasswordStorageDisabled(SVNURL url) { + // ++ myNumPasswordsWarn; + // } + // + // @Override + // public void warnOnSSLPassphraseStorageDisabled(SVNURL url) { + // ++ myNumSSLWarn; + // } + // + // @Override + // public boolean promptForSSLPlaintextPassphraseSaving(SVNURL url, String realm, File certificateFile, String certificateName) { + // ++ myNumSSLPlaintextPrompt; + // return mySSLPlaintextAnswer; + // } + // + // public int getNumAuthWarn() { + // return myNumAuthWarn; + // } + // + // public int getNumPasswordsWarn() { + // return myNumPasswordsWarn; + // } + // + // public int getNumPlaintextPrompt() { + // return myNumPlaintextPrompt; + // } + // + // public int getNumSSLPlaintextPrompt() { + // return myNumSSLPlaintextPrompt; + // } + // + // public int getNumSSLWarn() { + // return myNumSSLWarn; + // } + //} } From 4b0538dce99aaf9bca8c13b45a0e91cbcc25397c Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Fri, 20 Jul 2012 22:08:34 +0200 Subject: [PATCH 08/40] 1. fix npe in easy-mode 2. in easy mode correctly update storages --- .../incremental/storage/BuildDataManager.java | 2 +- .../ether/dependencyView/Mappings.java | 70 ++++++++++--------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java index d3ff62d8d5bb..ce347c856930 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java @@ -19,7 +19,7 @@ import java.util.Map; * Date: 10/7/11 */ public class BuildDataManager implements StorageOwner { - private static final int VERSION = 7; + private static final int VERSION = 8; private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.storage.BuildDataManager"); private static final String SRC_TO_OUTPUTS_STORAGE = "src-out"; private static final String SRC_TO_FORM_STORAGE = "src-form"; diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java index efff3e4a93e3..5650e1b43579 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java @@ -780,7 +780,7 @@ public class Mappings { final Util myFuture; final Util myPresent; - final boolean myPreprocessOnly; + final boolean myEasyMode; // true means: no need to search for affected files, only preprocess data for integrate private class DelayedWorks { class Triple { @@ -820,7 +820,7 @@ public class Mappings { myQueue.add(new Triple(ownerClass, changedField, future)); } - boolean doWork(final Collection affectedFiles) { + boolean doWork(@NotNull final Collection affectedFiles) { if (!myQueue.isEmpty()) { debug("Starting delayed works."); @@ -888,7 +888,7 @@ public class Mappings { myFuture = null; myPresent = null; - myPreprocessOnly = true; + myEasyMode = true; delta.myIsRebuild = true; } @@ -907,7 +907,7 @@ public class Mappings { myFuture = new Util(delta); myPresent = new Util(); - myPreprocessOnly = true; + myEasyMode = true; } private Differential(final Mappings delta, @@ -931,22 +931,24 @@ public class Mappings { myFuture = new Util(delta); myPresent = new Util(); - myPreprocessOnly = false; + myEasyMode = false; } private void processDisappearedClasses() { myDelta.compensateRemovedContent(myFilesToCompile); - final Collection removed = myDelta.myRemovedFiles; + if (!myEasyMode) { + final Collection removed = myDelta.myRemovedFiles; - if (removed != null) { - for (final String file : removed) { - final Collection classes = mySourceFileToClasses.get(myContext.get(file)); + if (removed != null) { + for (final String file : removed) { + final Collection classes = mySourceFileToClasses.get(myContext.get(file)); - if (classes != null) { - for (ClassRepr c : classes) { - debug("Affecting usages of removed class ", c.name); - affectAll(c.name, myAffectedFiles, myFilter); + if (classes != null) { + for (ClassRepr c : classes) { + debug("Affecting usages of removed class ", c.name); + affectAll(c.name, myAffectedFiles, myFilter); + } } } } @@ -1542,8 +1544,8 @@ public class Mappings { } } - if (myPreprocessOnly) { - return false; + if (myEasyMode) { + continue; } myPresent.appendDependents(it, state.myDependants); @@ -1657,7 +1659,7 @@ public class Mappings { } debug("End of changed classes processing"); - return true; + return !myEasyMode; } private void processRemovedClases(final DiffState state) { @@ -1671,7 +1673,7 @@ public class Mappings { myDelta.myChangedFiles.add(fileName); } - if (!myPreprocessOnly) { + if (!myEasyMode) { myPresent.appendDependents(c, state.myDependants); debug("Adding usages of class ", c.name); state.myAffectedUsages.add(c.createUsage()); @@ -1690,7 +1692,7 @@ public class Mappings { myDelta.registerAddedSuperClass(c.name, sup); } - if (!myPreprocessOnly) { + if (!myEasyMode) { final TIntHashSet depClasses = myClassToClassDependency.get(c.name); if (depClasses != null) { @@ -1799,7 +1801,7 @@ public class Mappings { } debug("Begin of Differentiate:"); - debug("Easy mode: ", myPreprocessOnly); + debug("Easy mode: ", myEasyMode); processDisappearedClasses(); @@ -1818,34 +1820,34 @@ public class Mappings { final Set pastClasses = (Set)mySourceFileToClasses.get(fileName); final DiffState state = new DiffState(Difference.make(pastClasses, classes)); - if (!processChangedClasses(state) && !myPreprocessOnly) { - // turning non-incremental - return false; + if (!processChangedClasses(state)) { + if (!myEasyMode) { + // turning non-incremental + return false; + } } processRemovedClases(state); processAddedClasses(state); - if (!myPreprocessOnly) { + if (!myEasyMode) { calculateAffectedFiles(state); } } debug("End of Differentiate."); - if (!myPreprocessOnly) { - final Collection removed = myDelta.myRemovedFiles; - if (removed != null) { - for (final String r : removed) { - myAffectedFiles.remove(new File(r)); - } - } - - return myDelayedWorks.doWork(myAffectedFiles); - } - else { + if (myEasyMode) { return false; } + + final Collection removed = myDelta.myRemovedFiles; + if (removed != null) { + for (final String r : removed) { + myAffectedFiles.remove(new File(r)); + } + } + return myDelayedWorks.doWork(myAffectedFiles); } } } From b677d2ac181ff07390ed0d72f95d82bcee4ef754 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 20 Jul 2012 22:22:41 +0200 Subject: [PATCH 09/40] hopefully fix JavaAutoPopupTest blinks --- .../intellij/codeInsight/completion/JavaAutoPopupTest.groovy | 5 +++++ .../codeInsight/completion/CompletionProgressIndicator.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index 8f8b427b5e14..17c3940ac54d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -45,6 +45,8 @@ import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.progress.ProgressManager import com.intellij.psi.PsiFile import com.intellij.psi.PsiJavaFile +import junit.framework.Test +import junit.framework.TestSuite /** * @author peter @@ -560,6 +562,7 @@ public interface Test { assert myFixture.editor.caretModel.offset == offset + 1 joinAutopopup() joinCompletion() + assert !lookup.calculating assertContains "iterable" assertEquals 'iterable', lookup.currentItem.lookupString @@ -567,12 +570,14 @@ public interface Test { assert myFixture.editor.caretModel.offset == offset joinAutopopup() joinCompletion() + assert !lookup.calculating assertContains "if", "iterable", "int" assertEquals 'iterable', lookup.currentItem.lookupString edt { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_MOVE_CARET_LEFT) } joinAutopopup() joinCompletion() + assert !lookup.calculating assert lookup.items.size() > 3 for (i in 0.."iter".size()) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index 9ddcd39a661f..312f5e3a3bb1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -652,6 +652,11 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement hideAutopopupIfMeaningless(); + CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase(); + if (oldPhase instanceof CompletionPhase.CommittingDocuments) { + ((CompletionPhase.CommittingDocuments)oldPhase).replaced = true; + } + final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(this, myEditor); CompletionServiceImpl.setCompletionPhase(phase); From 83727a31d8a7a97fff83d7481be17e2f0085e07e Mon Sep 17 00:00:00 2001 From: Kirill Safonov Date: Sat, 21 Jul 2012 10:47:40 +0400 Subject: [PATCH 10/40] Jade support initial --- .../src/com/intellij/testFramework/LexerTestCase.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/testFramework/src/com/intellij/testFramework/LexerTestCase.java b/platform/testFramework/src/com/intellij/testFramework/LexerTestCase.java index ed4f46179fba..78c2a6a94525 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LexerTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/LexerTestCase.java @@ -93,7 +93,8 @@ public abstract class LexerTestCase extends UsefulTestCase { String fileName = PathManager.getHomePath() + "/" + getDirPath() + "/" + getTestName(true) + "." + fileExt; String text = ""; try { - text = StringUtil.convertLineSeparators(FileUtil.loadFile(new File(fileName)).trim()); + String fileText = FileUtil.loadFile(new File(fileName)); + text = StringUtil.convertLineSeparators(shouldTrim() ? fileText.trim() : fileText); } catch (IOException e) { fail("can't load file " + fileName + ": " + e.getMessage()); @@ -101,6 +102,10 @@ public abstract class LexerTestCase extends UsefulTestCase { doTest(text); } + protected boolean shouldTrim() { + return true; + } + private static String getTokenText(Lexer lexer) { String text = lexer.getBufferSequence().subSequence(lexer.getTokenStart(), lexer.getTokenEnd()).toString(); text = StringUtil.replace(text, "\n", "\\n"); From 66b1ea4148b2eab29201f6b5dd06be4e35791e33 Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Sat, 21 Jul 2012 20:27:30 +0400 Subject: [PATCH 11/40] Refactoring --- .../ExternalPSIChangeListener.java | 139 ++------------- .../ExternalPSIChangeListener.java | 159 ++++++++++++++++++ 2 files changed, 170 insertions(+), 128 deletions(-) create mode 100644 plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/ExternalPSIChangeListener.java diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/ExternalPSIChangeListener.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/ExternalPSIChangeListener.java index df73f2ad577b..536060dff4a6 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/designSurface/ExternalPSIChangeListener.java +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/ExternalPSIChangeListener.java @@ -15,119 +15,38 @@ */ package com.intellij.android.designer.designSurface; -import com.intellij.openapi.application.ModalityState; +import com.intellij.designer.designSurface.DesignerEditorPanel; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.psi.PsiTreeChangeAdapter; import com.intellij.psi.PsiTreeChangeEvent; -import com.intellij.util.Alarm; -import com.intellij.util.containers.ComparatorUtil; import org.jetbrains.android.facet.AndroidFacet; -import org.jetbrains.annotations.NotNull; /** * @author Alexander Lobas */ -public class ExternalPSIChangeListener extends PsiTreeChangeAdapter { - private final Alarm myAlarm = new Alarm(); - private final AndroidDesignerEditorPanel myDesigner; - private final PsiFile myFile; - private final int myDelayMillis; - private final Runnable myRunnable; - private volatile boolean myRunState; - private volatile boolean myInitialize; - private String myContent; +public class ExternalPSIChangeListener extends com.intellij.designer.designSurface.ExternalPSIChangeListener { private VirtualFile[] myResourceDepends; - private boolean myUpdateRenderer; - public ExternalPSIChangeListener(AndroidDesignerEditorPanel designer, PsiFile file, int delayMillis, Runnable runnable) { - myDesigner = designer; - myFile = file; - myDelayMillis = delayMillis; - myRunnable = runnable; - myContent = myDesigner.getEditorText(); - PsiManager.getInstance(myDesigner.getProject()).addPsiTreeChangeListener(this); - } - - public void setInitialize() { - myInitialize = true; - } - - public void start() { - if (!myRunState) { - myRunState = true; - } - } - - public void dispose() { - PsiManager.getInstance(myDesigner.getProject()).removePsiTreeChangeListener(this); - stop(); - } - - public void stop() { - if (myRunState) { - myRunState = false; - clear(); - } - } - - public void activate() { - if (!myRunState) { - start(); - if (!ComparatorUtil.equalsNullable(myContent, myDesigner.getEditorText()) || myDesigner.getRootComponent() == null) { - myUpdateRenderer = false; - addRequest(); - } - myContent = null; - } + public ExternalPSIChangeListener(DesignerEditorPanel designer, PsiFile file, int delayMillis, Runnable runnable) { + super(designer, file, delayMillis, runnable); } + @Override public void deactivate() { - if (myRunState) { - stop(); - myContent = myDesigner.getEditorText(); - } - - myUpdateRenderer = false; + super.deactivate(); if (!myDesigner.getProject().isDisposed()) { myResourceDepends = AndroidFacet.getInstance(myDesigner.getModule()).getLocalResourceManager().getAllResourceDirs(); } } - public void addRequest() { - addRequest(myRunnable); - } + @Override + protected void updatePsi(PsiTreeChangeEvent event) { + boolean runState = myRunState; + super.updatePsi(event); - public boolean isUpdateRenderer() { - return myUpdateRenderer; - } - - public void addRequest(final Runnable runnable) { - clear(); - myAlarm.addRequest(new Runnable() { - @Override - public void run() { - if (myRunState && myInitialize && !myDesigner.getProject().isDisposed()) { - runnable.run(); - } - } - }, myDelayMillis, ModalityState.stateForComponent(myDesigner)); - } - - public void clear() { - myAlarm.cancelAllRequests(); - } - - private void updatePsi(PsiTreeChangeEvent event) { - if (myRunState) { - if (myFile == event.getFile()) { - addRequest(); - } - } - else if (myResourceDepends != null && !myUpdateRenderer) { + if (!runState && myResourceDepends != null && !myUpdateRenderer) { PsiFile psiFile = event.getFile(); if (psiFile == null) { return; @@ -144,40 +63,4 @@ public class ExternalPSIChangeListener extends PsiTreeChangeAdapter { } } } - - ////////////////////////////////////////////////////////////////////////////////////////// - // - // PSI - // - ////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void childAdded(@NotNull PsiTreeChangeEvent event) { - updatePsi(event); - } - - @Override - public void childRemoved(@NotNull PsiTreeChangeEvent event) { - updatePsi(event); - } - - @Override - public void childReplaced(@NotNull PsiTreeChangeEvent event) { - updatePsi(event); - } - - @Override - public void childMoved(@NotNull PsiTreeChangeEvent event) { - updatePsi(event); - } - - @Override - public void childrenChanged(@NotNull PsiTreeChangeEvent event) { - updatePsi(event); - } - - @Override - public void propertyChanged(@NotNull PsiTreeChangeEvent event) { - updatePsi(event); - } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/ExternalPSIChangeListener.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/ExternalPSIChangeListener.java new file mode 100644 index 000000000000..ff4e54741edd --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/ExternalPSIChangeListener.java @@ -0,0 +1,159 @@ +/* + * 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.designer.designSurface; + +import com.intellij.openapi.application.ModalityState; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiTreeChangeAdapter; +import com.intellij.psi.PsiTreeChangeEvent; +import com.intellij.util.Alarm; +import com.intellij.util.containers.ComparatorUtil; +import org.jetbrains.annotations.NotNull; + +/** + * @author Alexander Lobas + */ +public class ExternalPSIChangeListener extends PsiTreeChangeAdapter { + private final Alarm myAlarm = new Alarm(); + protected final DesignerEditorPanel myDesigner; + private final PsiFile myFile; + private final int myDelayMillis; + private final Runnable myRunnable; + protected volatile boolean myRunState; + private volatile boolean myInitialize; + private String myContent; + protected boolean myUpdateRenderer; + + public ExternalPSIChangeListener(DesignerEditorPanel designer, PsiFile file, int delayMillis, Runnable runnable) { + myDesigner = designer; + myFile = file; + myDelayMillis = delayMillis; + myRunnable = runnable; + myContent = myDesigner.getEditorText(); + PsiManager.getInstance(myDesigner.getProject()).addPsiTreeChangeListener(this); + } + + public void setInitialize() { + myInitialize = true; + } + + public void start() { + if (!myRunState) { + myRunState = true; + } + } + + public void dispose() { + PsiManager.getInstance(myDesigner.getProject()).removePsiTreeChangeListener(this); + stop(); + } + + public void stop() { + if (myRunState) { + myRunState = false; + clear(); + } + } + + public void activate() { + if (!myRunState) { + start(); + if (!ComparatorUtil.equalsNullable(myContent, myDesigner.getEditorText()) || myDesigner.getRootComponent() == null) { + myUpdateRenderer = false; + addRequest(); + } + myContent = null; + } + } + + public void deactivate() { + if (myRunState) { + stop(); + myContent = myDesigner.getEditorText(); + } + + myUpdateRenderer = false; + } + + public void addRequest() { + addRequest(myRunnable); + } + + public boolean isUpdateRenderer() { + return myUpdateRenderer; + } + + public void addRequest(final Runnable runnable) { + clear(); + myAlarm.addRequest(new Runnable() { + @Override + public void run() { + if (myRunState && myInitialize && !myDesigner.getProject().isDisposed()) { + runnable.run(); + } + } + }, myDelayMillis, ModalityState.stateForComponent(myDesigner)); + } + + public void clear() { + myAlarm.cancelAllRequests(); + } + + protected void updatePsi(PsiTreeChangeEvent event) { + if (myRunState) { + if (myFile == event.getFile()) { + addRequest(); + } + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // PSI + // + ////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void childAdded(@NotNull PsiTreeChangeEvent event) { + updatePsi(event); + } + + @Override + public void childRemoved(@NotNull PsiTreeChangeEvent event) { + updatePsi(event); + } + + @Override + public void childReplaced(@NotNull PsiTreeChangeEvent event) { + updatePsi(event); + } + + @Override + public void childMoved(@NotNull PsiTreeChangeEvent event) { + updatePsi(event); + } + + @Override + public void childrenChanged(@NotNull PsiTreeChangeEvent event) { + updatePsi(event); + } + + @Override + public void propertyChanged(@NotNull PsiTreeChangeEvent event) { + updatePsi(event); + } +} \ No newline at end of file From 174c11044be1c5aa8c7e86005e8a7089eef2ad40 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 22 Jul 2012 14:31:40 +0400 Subject: [PATCH 12/40] [git] IDEA-89051 Move file between Git repositories: deleted file does not appear in Local Changes VcsDirtyScopeVfsListener: the old path of a moved file should be treated as deleted (when creating a FilePath), because the VirtualFile will change after move. --- .../intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.java index 980c8fcbe2b0..74672c447561 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.java @@ -136,7 +136,7 @@ public class VcsDirtyScopeVfsListener implements ApplicationComponent, BulkFileL if (!file.isInLocalFileSystem()) { continue; } dirtyFilesAndDirs.add(file, true); } else if (event instanceof VFileMoveEvent || event instanceof VFilePropertyChangeEvent) { - dirtyFilesAndDirs.add(file, false); + dirtyFilesAndDirs.add(file, true); } } // and notify VCSDirtyScopeManager From 27307481f7b45ef0cf4f3b8ccadbe09c2765e4df Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 18 Jul 2012 19:24:35 +0400 Subject: [PATCH 13/40] [merge tool] cleanup, @Nullable, dead code --- .../com/intellij/openapi/diff/DiffViewer.java | 3 ++ .../openapi/diff/impl/CompositeDiffPanel.java | 2 ++ .../openapi/diff/impl/DiffPanelImpl.java | 1 + .../openapi/diff/impl/EditingSides.java | 2 ++ .../openapi/diff/impl/EmptyDiffViewer.java | 2 ++ .../impl/incrementalMerge/ui/EditorPlace.java | 2 +- .../impl/incrementalMerge/ui/MergePanel2.java | 31 +++++++++---------- .../openapi/diff/impl/util/FocusDiffSide.java | 2 ++ 8 files changed, 27 insertions(+), 18 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/diff/DiffViewer.java b/platform/platform-api/src/com/intellij/openapi/diff/DiffViewer.java index 649a519b7e9b..e3aa21f43f38 100644 --- a/platform/platform-api/src/com/intellij/openapi/diff/DiffViewer.java +++ b/platform/platform-api/src/com/intellij/openapi/diff/DiffViewer.java @@ -15,6 +15,8 @@ */ package com.intellij.openapi.diff; +import org.jetbrains.annotations.Nullable; + import javax.swing.*; /** @@ -25,6 +27,7 @@ public interface DiffViewer { JComponent getComponent(); + @Nullable JComponent getPreferredFocusedComponent(); int getContentsNumber(); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/CompositeDiffPanel.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/CompositeDiffPanel.java index 4285f6d44290..89cfda72bf1e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/CompositeDiffPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/CompositeDiffPanel.java @@ -25,6 +25,7 @@ import com.intellij.openapi.diff.impl.external.DiscloseMultiRequest; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.ui.content.Content; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -121,6 +122,7 @@ public class CompositeDiffPanel implements DiffViewer { return myUi.getComponent(); } + @Nullable @Override public JComponent getPreferredFocusedComponent() { final Content[] contents = myUi.getContents(); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java index 918b3f1a6609..0048b0e3a18b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java @@ -369,6 +369,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid return getLineBlocks().getCount() > 0 || myNotCalculateDiffPanel != null; } + @Nullable public JComponent getPreferredFocusedComponent() { return myCurrentSide.getFocusableComponent(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/EditingSides.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/EditingSides.java index 17f29833b42f..fec2c0e36860 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/EditingSides.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/EditingSides.java @@ -18,8 +18,10 @@ package com.intellij.openapi.diff.impl; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.diff.impl.splitter.LineBlocks; import com.intellij.openapi.editor.Editor; +import org.jetbrains.annotations.Nullable; public interface EditingSides { + @Nullable Editor getEditor(FragmentSide side); LineBlocks getLineBlocks(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/EmptyDiffViewer.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/EmptyDiffViewer.java index f2ed2a825f85..44be87d9641b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/EmptyDiffViewer.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/EmptyDiffViewer.java @@ -21,6 +21,7 @@ import com.intellij.openapi.diff.DiffViewer; import com.intellij.openapi.diff.DiffViewerType; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -46,6 +47,7 @@ public class EmptyDiffViewer implements DiffViewer { return wrapper; } + @Nullable @Override public JComponent getPreferredFocusedComponent() { return null; diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java index 92a2f318ea66..d641918194f1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/EditorPlace.java @@ -237,7 +237,7 @@ public class EditorPlace extends JComponent implements Disposable, EditorEx.Repa return myEditor; } - public void setDocument(Document document) { + public void setDocument(@Nullable Document document) { if (document == getDocument()) return; removeEditor(); myState.setDocument(document); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index 219c3f9e0e7a..0e720e0d6c76 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -177,6 +177,7 @@ public class MergePanel2 implements DiffViewer { /** * @deprecated Because it references by index. */ + @Nullable @Deprecated public Editor getEditor(int index) { return getEditorPlace(index).getEditor(); @@ -265,20 +266,9 @@ public class MergePanel2 implements DiffViewer { } Editor editor = place.getEditor(); DiffEditorState editorState = place.getState(); - ((EditorEx)editor).setHighlighter(EditorHighlighterFactory.getInstance(). - createEditorHighlighter(editorState.getFileType(), settings, editorState.getProject())); - } - - public void setAdditionalLinesAndColumns(int lines, int columns) { - for (Editor editor : getEditors()) { - editor.getSettings().setAdditionalLinesCount(lines); - editor.getSettings().setAdditionalColumnsCount(columns); - } - } - - public void setShowLineNumbers(boolean show) { - for (Editor editor : getEditors()) { - MergeToolEditorSetting.LINE_NUMBERS.apply(editor, show); + if (editor != null) { + ((EditorEx)editor).setHighlighter(EditorHighlighterFactory.getInstance(). + createEditorHighlighter(editorState.getFileType(), settings, editorState.getProject())); } } @@ -339,6 +329,7 @@ public class MergePanel2 implements DiffViewer { parent.setTitle(windowTitle); } + @Nullable private JDialog getDialogWrapperParent() { Component panel = myPanel; while (panel != null){ @@ -352,6 +343,7 @@ public class MergePanel2 implements DiffViewer { return myPanel; } + @Nullable public JComponent getPreferredFocusedComponent() { return getEditorPlace(1).getContentComponent(); } @@ -372,6 +364,7 @@ public class MergePanel2 implements DiffViewer { return true; } + @Nullable public MergeRequestImpl getMergeRequest() { return (MergeRequestImpl)(myData instanceof MergeRequestImpl ? myData : null); } @@ -383,6 +376,7 @@ public class MergePanel2 implements DiffViewer { mySide = side; } + @Nullable public Editor getEditor(FragmentSide side) { return MergePanel2.this.getEditor(mySide.getIndex() + side.getIndex()); } @@ -399,8 +393,8 @@ public class MergePanel2 implements DiffViewer { if (centerComponent.isShowing()) { centerComponent.requestFocus(); } - int[] toLeft = getPrimaryBegginings(myDividers[0].getPaint()); - int[] toRight = getPrimaryBegginings(myDividers[1].getPaint()); + int[] toLeft = getPrimaryBeginnings(myDividers[0].getPaint()); + int[] toRight = getPrimaryBeginnings(myDividers[1].getPaint()); int line; if (toLeft.length > 0 && toRight.length > 0) { line = Math.min(toLeft[0], toRight[0]); @@ -417,7 +411,7 @@ public class MergePanel2 implements DiffViewer { SyncScrollSupport.scrollEditor(centerEditor, line); } - private int[] getPrimaryBegginings(DiffDividerPaint paint) { + private int[] getPrimaryBeginnings(DiffDividerPaint paint) { FragmentSide primarySide = paint.getLeftSide(); LOG.assertTrue(getEditor(1) == paint.getSides().getEditor(primarySide)); return paint.getSides().getLineBlocks().getBegginings(primarySide); @@ -466,6 +460,7 @@ public class MergePanel2 implements DiffViewer { return getContentType(); } + @Nullable public Project getProject() { return myData == null ? null : myData.getProject(); } @@ -512,6 +507,7 @@ public class MergePanel2 implements DiffViewer { mySide = side; } + @Nullable public Editor getEditor() { return MergePanel2.this.getEditor(mySide.getMergeIndex()); } @@ -540,6 +536,7 @@ public class MergePanel2 implements DiffViewer { } } + @Nullable public static MergePanel2 fromDataContext(DataContext dataContext) { DiffViewer diffComponent = PlatformDataKeys.DIFF_VIEWER.getData(dataContext); return diffComponent instanceof MergePanel2 ? (MergePanel2)diffComponent : null; diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/FocusDiffSide.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/FocusDiffSide.java index 26e793c63f1e..a2bec9f9c99a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/FocusDiffSide.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/util/FocusDiffSide.java @@ -17,11 +17,13 @@ package com.intellij.openapi.diff.impl.util; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.editor.Editor; +import org.jetbrains.annotations.Nullable; public interface FocusDiffSide { DataKey DATA_KEY = DataKey.create("focusedDiffSide"); @Deprecated String FOCUSED_DIFF_SIDE = DATA_KEY.getName(); + @Nullable Editor getEditor(); int[] getFragmentStartingLines(); From 09fc04f2447e9bbd2b62c14488f3fad4f99498f5 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 19 Jul 2012 13:23:19 +0400 Subject: [PATCH 14/40] [merge tool] fix a misprint in the method name --- .../src/com/intellij/openapi/diff/impl/DiffPanelImpl.java | 2 +- .../openapi/diff/impl/incrementalMerge/ui/MergePanel2.java | 6 +++--- .../com/intellij/openapi/diff/impl/splitter/LineBlocks.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java index 0048b0e3a18b..23d264f6e0c4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java @@ -335,7 +335,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid } int[] getFragmentBeginnings(FragmentSide side) { - return getLineBlocks().getBegginings(side); + return getLineBlocks().getBeginnings(side); } public void dispose() { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index 0e720e0d6c76..d7015c24be79 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -414,7 +414,7 @@ public class MergePanel2 implements DiffViewer { private int[] getPrimaryBeginnings(DiffDividerPaint paint) { FragmentSide primarySide = paint.getLeftSide(); LOG.assertTrue(getEditor(1) == paint.getSides().getEditor(primarySide)); - return paint.getSides().getLineBlocks().getBegginings(primarySide); + return paint.getSides().getLineBlocks().getBeginnings(primarySide); } } @@ -513,7 +513,7 @@ public class MergePanel2 implements DiffViewer { } public int[] getFragmentStartingLines() { - return myMergeList.getChanges(mySide).getLineBlocks().getBegginings(MergeList.BRANCH_SIDE); + return myMergeList.getChanges(mySide).getLineBlocks().getBeginnings(MergeList.BRANCH_SIDE); } } @@ -527,7 +527,7 @@ public class MergePanel2 implements DiffViewer { if (myMergeList != null) { for (int i = 0; i < 2; i++) { FragmentSide branchSide = FragmentSide.fromIndex(i); - beginnings.addAll(myMergeList.getChanges(branchSide).getLineBlocks().getBegginings(MergeList.BASE_SIDE)); + beginnings.addAll(myMergeList.getChanges(branchSide).getLineBlocks().getBeginnings(MergeList.BASE_SIDE)); } } int[] result = beginnings.toArray(); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java index 3bb4c4f44104..59066442a1ff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java @@ -60,10 +60,10 @@ public class LineBlocks { } public int getCount() { - return getBegginings(FragmentSide.SIDE1).length; + return getBeginnings(FragmentSide.SIDE1).length; } - public int[] getBegginings(FragmentSide side) { + public int[] getBeginnings(FragmentSide side) { IntArrayList result = new IntArrayList(getIntervals(side).length); int previousBeginning = Integer.MIN_VALUE; Interval[] sideIntervals = getIntervals(side); From ba7ac90ad543c471a8a2490fd82484625d482996 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 19 Jul 2012 14:15:21 +0400 Subject: [PATCH 15/40] [diff/merge] LineBlocks internal refactoring Don't use provider: all the data is ready at init; Don't work with 3 arrays, use a list of custom Diff structure instead. --- .../diff/impl/splitter/LineBlocks.java | 88 +++++++++---------- .../diff/impl/fragments/LineBlock.java | 2 +- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java index 59066442a1ff..ad7ea57443f1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java @@ -28,14 +28,12 @@ import org.jetbrains.annotations.NotNull; import java.util.*; public class LineBlocks { - public static final LineBlocks EMPTY = new LineBlocks(SimpleIntervalProvider.EMPTY, new TextDiffType[0]); + public static final LineBlocks EMPTY = new LineBlocks(Collections.emptyList()); private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.splitter.LineBlocks"); - private final IntervalsProvider myIntervalsProvider; - private final TextDiffType[] myTypes; + private final List myDiffs; - private LineBlocks(IntervalsProvider intervalsProvider, TextDiffType[] types) { - myIntervalsProvider = intervalsProvider; - myTypes = types; + private LineBlocks(List diffs) { + myDiffs = diffs; } public Interval getVisibleIndices(Trapezium visibleArea) { @@ -56,7 +54,7 @@ public class LineBlocks { } public TextDiffType getType(int index) { - return myTypes[index]; + return myDiffs.get(index).getDiffType(); } public int getCount() { @@ -99,7 +97,19 @@ public class LineBlocks { } public Interval[] getIntervals(FragmentSide side) { - return myIntervalsProvider.getIntervals(side); + Interval[] intervals = new Interval[myDiffs.size()]; + for (int i = 0; i < intervals.length; i++) { + intervals[i] = myDiffs.get(i).getInterval(side); + } + return intervals; + } + + public TextDiffType[] getTypes() { + TextDiffType[] types = new TextDiffType[myDiffs.size()]; + for (int i = 0; i < types.length; i++) { + types[i] = myDiffs.get(i).getDiffType(); + } + return types; } private int transform(int location, Interval[] domain, Interval[] range) { @@ -147,20 +157,13 @@ public class LineBlocks { static LineBlocks createLineBlocks(LineBlock[] blocks) { Arrays.sort(blocks, LineBlock.COMPARATOR); - Interval[] intervals1 = new Interval[blocks.length]; - Interval[] intervals2 = new Interval[blocks.length]; - TextDiffType[] types = new TextDiffType[blocks.length]; - for (int i = 0; i < blocks.length; i++) { - LineBlock block = blocks[i]; - intervals1[i] = new Interval(block.getStartingLine1(), block.getModifiedLines1()); - intervals2[i] = new Interval(block.getStartingLine2(), block.getModifiedLines2()); - types[i] = TextDiffType.create(block.getType()); + List diffs = new ArrayList(blocks.length); + for (LineBlock block : blocks) { + Interval interval1 = new Interval(block.getStartingLine1(), block.getModifiedLines1()); + Interval interval2 = new Interval(block.getStartingLine2(), block.getModifiedLines2()); + diffs.add(new Diff(interval1, interval2, TextDiffType.create(block.getType()))); } - return create(intervals1, intervals2, types); - } - - private static LineBlocks create(Interval[] intervals1, Interval[] intervals2, TextDiffType[] types) { - return new LineBlocks(new SimpleIntervalProvider(intervals1, intervals2), types); + return new LineBlocks(diffs); } @NotNull @@ -168,44 +171,41 @@ public class LineBlocks { // changes may come mixed, need to sort them to get correct intervals Collections.sort(changes, ChangeList.CHANGE_ORDER); - ArrayList intervals1 = new ArrayList(); - ArrayList intervals2 = new ArrayList(); - ArrayList types = new ArrayList(); + List diffs = new ArrayList(changes.size()); for (Change change : changes) { if (!change.isValid()) { continue; } int start1 = change.getChangeSide(FragmentSide.SIDE1).getStartLine(); int end1 = change.getChangeSide(FragmentSide.SIDE1).getEndLine(); - intervals1.add(Interval.fromTo(start1, end1)); + Interval interval1 = Interval.fromTo(start1, end1); int start2 = change.getChangeSide(FragmentSide.SIDE2).getStartLine(); int end2 = change.getChangeSide(FragmentSide.SIDE2).getEndLine(); - intervals2.add(Interval.fromTo(start2, end2)); + Interval interval2 = Interval.fromTo(start2, end2); - types.add(change.getType().getTypeKey()); + diffs.add(new Diff(interval1, interval2, change.getType().getTypeKey())); } - return create(intervals1.toArray(new Interval[intervals1.size()]), - intervals2.toArray(new Interval[intervals2.size()]), types.toArray(new TextDiffType[types.size()])); + return new LineBlocks(diffs); } - public TextDiffType[] getTypes() { - return myTypes; - } + private static class Diff { - public interface IntervalsProvider { - Interval[] getIntervals(FragmentSide side); - } - - public static class SimpleIntervalProvider implements IntervalsProvider { - private final Interval[][] myIntervals = new Interval[2][]; - public static final IntervalsProvider EMPTY = new SimpleIntervalProvider(new Interval[0], new Interval[0]); + @NotNull private final Interval myIntervalForSide1; + @NotNull private final Interval myIntervalForSide2; + @NotNull private final TextDiffType myDiffType; - public SimpleIntervalProvider(Interval[] intervals1, Interval[] intervals2) { - myIntervals[0] = intervals1; - myIntervals[1] = intervals2; + private Diff(@NotNull Interval intervalForSide1, @NotNull Interval intervalForSide2, @NotNull TextDiffType type) { + myIntervalForSide1 = intervalForSide1; + myIntervalForSide2 = intervalForSide2; + myDiffType = type; } - public Interval[] getIntervals(FragmentSide side) { - return myIntervals[side.getIndex()]; + @NotNull + public TextDiffType getDiffType() { + return myDiffType; + } + + public Interval getInterval(FragmentSide side) { + return side == FragmentSide.SIDE1 ? myIntervalForSide1 : myIntervalForSide2; } } } diff --git a/platform/util/src/com/intellij/openapi/diff/impl/fragments/LineBlock.java b/platform/util/src/com/intellij/openapi/diff/impl/fragments/LineBlock.java index 6ff9de438e15..7c857d4f7fc8 100644 --- a/platform/util/src/com/intellij/openapi/diff/impl/fragments/LineBlock.java +++ b/platform/util/src/com/intellij/openapi/diff/impl/fragments/LineBlock.java @@ -58,7 +58,7 @@ public class LineBlock { return myStartingLine2 + myModifiedLines2; } - public static final Comparator COMPARATOR = new Comparator() { + public static final Comparator COMPARATOR = new Comparator() { public int compare(LineBlock block1, LineBlock block2) { return block1.getStartingLine1() - block2.getStartingLine1(); } From f2a6d145e0ce87f38b6de045abc6f7cd5c09ae14 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 22 Jul 2012 14:56:27 +0400 Subject: [PATCH 16/40] [diff/merge] IDEA-59513 scroll to the first conflicting change after applying "all non-conflicting changes" Let LineBlocks#getBeginnings return only applied changes, and use this method to scroll in the merge tool to ignore applied changes when scrolling to the first change. --- .../ui/ApplyNonConflicts.java | 10 +++++- .../impl/incrementalMerge/ui/MergePanel2.java | 4 +-- .../diff/impl/splitter/LineBlocks.java | 33 ++++++++++++++----- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/ApplyNonConflicts.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/ApplyNonConflicts.java index 8316b865e9cf..3449f490415b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/ApplyNonConflicts.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/ApplyNonConflicts.java @@ -22,17 +22,22 @@ import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.diff.DiffBundle; import com.intellij.openapi.diff.impl.incrementalMerge.Change; import com.intellij.openapi.diff.impl.incrementalMerge.MergeList; +import com.intellij.openapi.diff.impl.util.DiffPanelOuterComponent; import com.intellij.openapi.project.DumbAware; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FilteringIterator; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ApplyNonConflicts extends AnAction implements DumbAware { - public ApplyNonConflicts() { + @Nullable private final DiffPanelOuterComponent myDiffPanel; + + public ApplyNonConflicts(@Nullable DiffPanelOuterComponent diffPanel) { super(DiffBundle.message("merge.dialog.apply.all.non.conflicting.changes.action.name"), null, AllIcons.Diff.ApplyNotConflicts); + myDiffPanel = diffPanel; } public void actionPerformed(AnActionEvent e) { @@ -41,6 +46,9 @@ public class ApplyNonConflicts extends AnAction implements DumbAware { for (Change change : notConflicts) { Change.apply(change, MergeList.BRANCH_SIDE); } + if (myDiffPanel != null) { + myDiffPanel.requestScrollEditors(); + } } public void update(AnActionEvent e) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java index d7015c24be79..bfe6fea6f4e8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ui/MergePanel2.java @@ -145,7 +145,7 @@ public class MergePanel2 implements DiffViewer { toolbar.addAction(new OpenPartialDiffAction(1, 2, AllIcons.Diff.RightDiff)); toolbar.addAction(new OpenPartialDiffAction(0, 2, AllIcons.Diff.BranchDiff)); toolbar.addSeparator(); - toolbar.addAction(new ApplyNonConflicts()); + toolbar.addAction(new ApplyNonConflicts(myPanel)); toolbar.addSeparator(); toolbar.addAction(new MergeToolSettingsAction(getEditors())); } @@ -414,7 +414,7 @@ public class MergePanel2 implements DiffViewer { private int[] getPrimaryBeginnings(DiffDividerPaint paint) { FragmentSide primarySide = paint.getLeftSide(); LOG.assertTrue(getEditor(1) == paint.getSides().getEditor(primarySide)); - return paint.getSides().getLineBlocks().getBeginnings(primarySide); + return paint.getSides().getLineBlocks().getBeginnings(primarySide, true); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java index ad7ea57443f1..22ce1f403eb8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/splitter/LineBlocks.java @@ -22,7 +22,7 @@ import com.intellij.openapi.diff.impl.highlighting.FragmentSide; import com.intellij.openapi.diff.impl.incrementalMerge.Change; import com.intellij.openapi.diff.impl.incrementalMerge.ChangeList; import com.intellij.openapi.diff.impl.util.TextDiffType; -import com.intellij.util.containers.IntArrayList; +import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -61,16 +61,33 @@ public class LineBlocks { return getBeginnings(FragmentSide.SIDE1).length; } + /** + * Get beginnings of all the changes from the specified side. + * @param side Side of the changes. + */ public int[] getBeginnings(FragmentSide side) { - IntArrayList result = new IntArrayList(getIntervals(side).length); + return getBeginnings(side, false); + } + + /** + * Get beginnings of the changes from the specified side. + * @param side Side of the changes. + * @param unappliedOnly If true - only unapplied changes will be considered, if false - all changes (both applied and not applied). + */ + public int[] getBeginnings(FragmentSide side, boolean unappliedOnly) { + List result = new ArrayList(myDiffs.size()); int previousBeginning = Integer.MIN_VALUE; - Interval[] sideIntervals = getIntervals(side); - for (Interval sideInterval : sideIntervals) { - int start = sideInterval.getStart(); - if (start != previousBeginning) result.add(start); - previousBeginning = start; + + for (Diff diff : myDiffs) { + if (!unappliedOnly || !diff.getDiffType().isApplied()) { + Interval interval = diff.getInterval(side); + int start = interval.getStart(); + if (start != previousBeginning) result.add(start); + previousBeginning = start; + } } - return result.toArray(); + + return ArrayUtil.toIntArray(result); } static int getMaxStartedIndex(Interval[] intervals, int start) { From f8d8dfa0b13337d17a4a853a21bfc4590e28977e Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 22 Jul 2012 15:19:24 +0400 Subject: [PATCH 17/40] [git] Fix already disposed. IDEA-61919 --- plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java index ca4a9584975e..28559ff25bf0 100644 --- a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java +++ b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java @@ -157,7 +157,9 @@ public class GitFileAnnotation implements FileAnnotation { * Fire annotation changed event */ private void fireAnnotationChanged() { - myListeners.getMulticaster().onAnnotationChanged(); + if (!myProject.isDisposed()) { + myListeners.getMulticaster().onAnnotationChanged(); + } } /** From c96c97cf95f257ff9c7c8d0812d3f4ea605960b0 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 22 Jul 2012 17:07:29 +0400 Subject: [PATCH 18/40] [diff/merge] Fix divider polygons alignment for the short diff. Use a separate value for the short diff and for the full diff. --- .../src/com/intellij/openapi/diff/impl/DiffPanelImpl.java | 5 ++--- .../intellij/openapi/diff/impl/external/DiffManagerImpl.java | 3 ++- .../openapi/vcs/changes/ChangesFragmentedDiffPanel.java | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java index 23d264f6e0c4..2d5c01ef9473 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java @@ -77,7 +77,6 @@ import java.util.List; public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSidesContainer { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.DiffPanelImpl"); - public static final int DIFF_DIVIDER_POLYGONS_OFFSET = 3; private final DiffSplitterI mySplitter; private final DiffPanelOuterComponent myPanel; @@ -111,7 +110,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid private CanNotCalculateDiffPanel myNotCalculateDiffPanel; private final VisibleAreaListener myVisibleAreaListener; - public DiffPanelImpl(final Window owner, Project project, boolean enableToolbar, boolean horizontal) { + public DiffPanelImpl(final Window owner, Project project, boolean enableToolbar, boolean horizontal, int diffDividerPolygonsOffset) { myProject = project; myIsHorizontal = horizontal; myOptions = new DiffPanelOptions(this); @@ -130,7 +129,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid if (horizontal) { mySplitter = new DiffSplitter(myLeftSide.getComponent(), myRightSide.getComponent(), - new DiffDividerPaint(this, FragmentSide.SIDE1, DIFF_DIVIDER_POLYGONS_OFFSET), myData); + new DiffDividerPaint(this, FragmentSide.SIDE1, diffDividerPolygonsOffset), myData); } else { mySplitter = new HorizontalDiffSplitter(myLeftSide.getComponent(), myRightSide.getComponent()); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java index cc6bd6390640..cb93a4d49c78 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.Arrays; public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { + public static final int FULL_DIFF_DIVIDER_POLYGONS_OFFSET = 3; private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.external.DiffManagerImpl"); private static final Externalizer TOOL_PATH_UPDATE = new Externalizer() { @@ -137,7 +138,7 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable { } public DiffPanel createDiffPanel(Window window, Project project) { - return new DiffPanelImpl(window, project, true, true); + return new DiffPanelImpl(window, project, true, true, FULL_DIFF_DIVIDER_POLYGONS_OFFSET); } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java index ac61196b18c6..f50b4899bc8a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java @@ -79,6 +79,8 @@ import java.util.List; * Time: 4:00 PM */ public class ChangesFragmentedDiffPanel implements Disposable { + private static final int SHORT_DIFF_DIVIDER_POLYGONS_OFFSET = 2; + private final JPanel myPanel; private final Project myProject; private final JComponent myParent; @@ -346,7 +348,7 @@ public class ChangesFragmentedDiffPanel implements Disposable { } private DiffPanel createPanel(final boolean horizontal) { - final DiffPanel diffPanel = new DiffPanelImpl(null, myProject, false, horizontal){ + final DiffPanel diffPanel = new DiffPanelImpl(null, myProject, false, horizontal, SHORT_DIFF_DIVIDER_POLYGONS_OFFSET){ @Override protected DiffPanelState createDiffPanelState(@NotNull Disposable parentDisposable) { return new FragmentedDiffPanelState(this, myProject, ! horizontal, parentDisposable); From eca60281d9cb71c884f44b80db4f0f0ca632c01b Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 22 Jul 2012 15:18:55 +0200 Subject: [PATCH 19/40] IDEA-88947 Merge "Open Project" and "Open File" actions into one - Bring back Open Project to Welcome Screen --- .../src/com/intellij/ide/actions/OpenProjectAction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/idea-ui/src/com/intellij/ide/actions/OpenProjectAction.java b/java/idea-ui/src/com/intellij/ide/actions/OpenProjectAction.java index 03067054a6ed..c02b1bc16973 100644 --- a/java/idea-ui/src/com/intellij/ide/actions/OpenProjectAction.java +++ b/java/idea-ui/src/com/intellij/ide/actions/OpenProjectAction.java @@ -18,6 +18,7 @@ package com.intellij.ide.actions; import com.intellij.ide.IdeBundle; import com.intellij.ide.highlighter.ProjectFileType; import com.intellij.ide.impl.ProjectUtil; +import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; @@ -76,6 +77,6 @@ public class OpenProjectAction extends AnAction implements DumbAware { @Override public void update(AnActionEvent e) { super.update(e); - e.getPresentation().setVisible(false); + e.getPresentation().setVisible(ActionPlaces.WELCOME_SCREEN.equals(e.getPlace())); } } \ No newline at end of file From bb7d5180ecb10540d67574532538cf1ad93977df Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 22 Jul 2012 15:19:35 +0200 Subject: [PATCH 20/40] LookupElement.getPsiElement --- .../util/MethodParenthesesHandler.java | 11 +- .../codeInsight/lookup/LookupElement.java | 17 + .../completion/CompletionUtil.java | 541 +++++++++--------- .../LookupElementProximityWeigher.java | 3 +- .../lookup/DefaultLookupItemRenderer.java | 11 +- .../GrCompletionWithLibraryTest.groovy | 2 +- 6 files changed, 294 insertions(+), 291 deletions(-) diff --git a/java/openapi/src/com/intellij/codeInsight/completion/util/MethodParenthesesHandler.java b/java/openapi/src/com/intellij/codeInsight/completion/util/MethodParenthesesHandler.java index 815abeb03662..e1f83cb2707f 100644 --- a/java/openapi/src/com/intellij/codeInsight/completion/util/MethodParenthesesHandler.java +++ b/java/openapi/src/com/intellij/codeInsight/completion/util/MethodParenthesesHandler.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.completion.util; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; /** @@ -39,16 +40,16 @@ public class MethodParenthesesHandler extends ParenthesesInsertHandler 0; if (overloadsMatter){ - hasParams |= hasOverloads(item, allItems, method); + hasParams |= hasOverloads(allItems, method); } return hasParams; } - private static boolean hasOverloads(LookupElement item, LookupElement[] allItems, final PsiMethod method) { + private static boolean hasOverloads(LookupElement[] allItems, final PsiMethod method) { String name = method.getName(); - for (LookupElement item1 : allItems) { - final Object o = item1.getObject(); - if (item.getObject() != o && o instanceof PsiMethod && ((PsiMethod)o).getName().equals(name)) { + for (LookupElement another : allItems) { + final PsiElement element = another.getPsiElement(); + if (method != element && element instanceof PsiMethod && ((PsiMethod)element).getName().equals(name)) { return true; } } diff --git a/platform/lang-api/src/com/intellij/codeInsight/lookup/LookupElement.java b/platform/lang-api/src/com/intellij/codeInsight/lookup/LookupElement.java index 251607ec9adc..ea4346ea9f6e 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/lookup/LookupElement.java +++ b/platform/lang-api/src/com/intellij/codeInsight/lookup/LookupElement.java @@ -16,9 +16,11 @@ package com.intellij.codeInsight.lookup; import com.intellij.codeInsight.completion.InsertionContext; +import com.intellij.navigation.PsiElementNavigationItem; import com.intellij.openapi.util.ClassConditionKey; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.psi.PsiElement; +import com.intellij.psi.ResolveResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,6 +49,21 @@ public abstract class LookupElement extends UserDataHolderBase { return this; } + @Nullable + public PsiElement getPsiElement() { + Object o = getObject(); + if (o instanceof PsiElement) { + return (PsiElement)o; + } + if (o instanceof ResolveResult) { + return ((ResolveResult)o).getElement(); + } + if (o instanceof PsiElementNavigationItem) { + return ((PsiElementNavigationItem)o).getTargetElement(); + } + return null; + } + public boolean isValid() { final Object object = getObject(); if (object instanceof PsiElement) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java index 35fd24d1abdf..2ceecfb667af 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java @@ -1,275 +1,266 @@ -/* - * 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.codeInsight.completion; - -import com.intellij.codeInsight.CodeInsightSettings; -import com.intellij.codeInsight.TailType; -import com.intellij.codeInsight.lookup.Lookup; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupItem; -import com.intellij.codeInsight.lookup.LookupValueWithPsiElement; -import com.intellij.featureStatistics.FeatureUsageTracker; -import com.intellij.navigation.PsiElementNavigationItem; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.NotNullLazyValue; -import com.intellij.openapi.util.TextRange; -import com.intellij.patterns.ElementPattern; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.ResolveResult; -import com.intellij.psi.filters.TrueFilter; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.containers.HashMap; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import static com.intellij.patterns.PlatformPatterns.character; - -public class CompletionUtil { - static final Key RANGE_TRANSLATION = Key.create("completion.rangeTranslation"); - public static final Key TAIL_TYPE_ATTR = LookupItem.TAIL_TYPE_ATTR; - - private static final CompletionData ourGenericCompletionData = new CompletionData() { - { - final CompletionVariant variant = new CompletionVariant(PsiElement.class, TrueFilter.INSTANCE); - variant.addCompletionFilter(TrueFilter.INSTANCE, TailType.NONE); - registerVariant(variant); - } - }; - private static final HashMap> ourCustomCompletionDatas = new HashMap>(); - - public static final @NonNls String DUMMY_IDENTIFIER = CompletionInitializationContext.DUMMY_IDENTIFIER; - public static final @NonNls String DUMMY_IDENTIFIER_TRIMMED = DUMMY_IDENTIFIER.trim(); - - public static boolean startsWith(String text, String prefix) { - //if (text.length() <= prefix.length()) return false; - return toLowerCase(text).startsWith(toLowerCase(prefix)); - } - - private static String toLowerCase(String text) { - CodeInsightSettings settings = CodeInsightSettings.getInstance(); - switch (settings.COMPLETION_CASE_SENSITIVE) { - case CodeInsightSettings.NONE: - return text.toLowerCase(); - - case CodeInsightSettings.FIRST_LETTER: { - StringBuffer buffer = new StringBuffer(); - buffer.append(text.toLowerCase()); - if (buffer.length() > 0) { - buffer.setCharAt(0, text.charAt(0)); - } - return buffer.toString(); - } - - default: - return text; - } - } - - @Nullable - public static CompletionData getCompletionDataByElement(@Nullable final PsiElement position, @NotNull PsiFile originalFile) { - if (position == null) return null; - - final FileType fileType = position.getParent().getLanguage().getAssociatedFileType(); - if (fileType != null) { - final CompletionData mainData = getCompletionDataByFileType(fileType); - if (mainData != null) { - return mainData; - } - } - - final CompletionData mainData = getCompletionDataByFileType(originalFile.getFileType()); - return mainData != null ? mainData : ourGenericCompletionData; - } - - /** @see CompletionDataEP */ - @Deprecated - public static void registerCompletionData(FileType fileType, NotNullLazyValue completionData) { - ourCustomCompletionDatas.put(fileType, completionData); - } - - /** @see CompletionDataEP */ - @Deprecated - public static void registerCompletionData(FileType fileType, final CompletionData completionData) { - registerCompletionData(fileType, new NotNullLazyValue() { - @NotNull - protected CompletionData compute() { - return completionData; - } - }); - } - - @Nullable - public static CompletionData getCompletionDataByFileType(FileType fileType) { - for(CompletionDataEP ep: Extensions.getExtensions(CompletionDataEP.EP_NAME)) { - if (ep.fileType.equals(fileType.getName())) { - return ep.getHandler(); - } - } - final NotNullLazyValue lazyValue = ourCustomCompletionDatas.get(fileType); - return lazyValue == null ? null : lazyValue.getValue(); - } - - - public static boolean shouldShowFeature(final CompletionParameters parameters, @NonNls final String id) { - if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(id, parameters.getPosition().getProject())) { - FeatureUsageTracker.getInstance().triggerFeatureShown(id); - return true; - } - return false; - } - - public static String findJavaIdentifierPrefix(CompletionParameters parameters) { - return findJavaIdentifierPrefix(parameters.getPosition(), parameters.getOffset()); - } - - public static String findJavaIdentifierPrefix(final PsiElement insertedElement, final int offset) { - return findIdentifierPrefix(insertedElement, offset, character().javaIdentifierPart(), character().javaIdentifierStart()); - } - - public static String findReferenceOrAlphanumericPrefix(CompletionParameters parameters) { - String prefix = findReferencePrefix(parameters); - return prefix == null ? findAlphanumericPrefix(parameters) : prefix; - } - - public static String findAlphanumericPrefix(CompletionParameters parameters) { - return findIdentifierPrefix(parameters.getPosition().getContainingFile(), parameters.getOffset(), character().letterOrDigit(), character().letterOrDigit()); - } - - public static String findIdentifierPrefix(PsiElement insertedElement, int offset, ElementPattern idPart, - ElementPattern idStart) { - if(insertedElement == null) return ""; - final String text = insertedElement.getText(); - - final int offsetInElement = offset - insertedElement.getTextRange().getStartOffset(); - int start = offsetInElement - 1; - while (start >=0 ) { - if (!idPart.accepts(text.charAt(start))) break; - --start; - } - while (start + 1 < offsetInElement && !idStart.accepts(text.charAt(start + 1))) { - start++; - } - - return text.substring(start + 1, offsetInElement).trim(); - } - - @Nullable - public static String findReferencePrefix(CompletionParameters parameters) { - return CompletionData.getReferencePrefix(parameters.getPosition(), parameters.getOffset()); - } - - - static InsertionContext emulateInsertion(InsertionContext oldContext, int newStart, final LookupElement item) { - final InsertionContext newContext = newContext(oldContext, item); - emulateInsertion(item, newStart, newContext); - return newContext; - } - - private static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement) { - final Editor editor = oldContext.getEditor(); - return new InsertionContext(new OffsetMap(editor.getDocument()), Lookup.AUTO_INSERT_SELECT_CHAR, new LookupElement[]{forElement}, oldContext.getFile(), editor, - oldContext.shouldAddCompletionChar()); - } - - public static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement, int startOffset, int tailOffset) { - final InsertionContext context = newContext(oldContext, forElement); - setOffsets(context, startOffset, tailOffset); - return context; - } - - public static void emulateInsertion(LookupElement item, int offset, InsertionContext context) { - setOffsets(context, offset, offset); - - final Editor editor = context.getEditor(); - final Document document = editor.getDocument(); - final String lookupString = item.getLookupString(); - - document.insertString(offset, lookupString); - editor.getCaretModel().moveToOffset(context.getTailOffset()); - PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); - item.handleInsert(context); - } - - private static void setOffsets(InsertionContext context, int offset, final int tailOffset) { - final OffsetMap offsetMap = context.getOffsetMap(); - offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset); - offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, tailOffset); - offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, tailOffset); - context.setTailOffset(tailOffset); - } - - @Nullable - public static PsiElement getTargetElement(LookupElement lookupElement) { - Object object = lookupElement.getObject(); - if (object instanceof ResolveResult) { - object = ((ResolveResult)object).getElement(); - } - - if (object instanceof PsiElement) { - return getOriginalElement((PsiElement)object); - } - - if (object instanceof PsiElementNavigationItem) { - return ((PsiElementNavigationItem)object).getTargetElement(); - } - - if (object instanceof LookupValueWithPsiElement) { - final PsiElement element = ((LookupValueWithPsiElement)object).getElement(); - if (element != null) return getOriginalElement(element); - } - - return null; - } - - @Nullable - public static T getOriginalElement(@NotNull T psi) { - final PsiFile file = psi.getContainingFile(); - if (file != null && file != file.getOriginalFile() && psi.getTextRange() != null) { - TextRange range = psi.getTextRange(); - Integer start = range.getStartOffset(); - Integer end = range.getEndOffset(); - final Document document = file.getViewProvider().getDocument(); - if (document != null) { - final OffsetTranslator translator = document.getUserData(RANGE_TRANSLATION); - if (translator != null) { - start = translator.translateOffset(start); - end = translator.translateOffset(end); - if (start == null || end == null) { - return null; - } - } - } - //noinspection unchecked - return (T)PsiTreeUtil.findElementOfClassAtRange(file.getOriginalFile(), start, end, psi.getClass()); - } - - return psi; - } - - @NotNull - public static T getOriginalOrSelf(@NotNull T psi) { - final T element = getOriginalElement(psi); - return element == null ? psi : element; - } -} +/* + * 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.codeInsight.completion; + +import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.codeInsight.TailType; +import com.intellij.codeInsight.lookup.Lookup; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupItem; +import com.intellij.codeInsight.lookup.LookupValueWithPsiElement; +import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.NotNullLazyValue; +import com.intellij.openapi.util.TextRange; +import com.intellij.patterns.ElementPattern; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.filters.TrueFilter; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.HashMap; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.patterns.PlatformPatterns.character; + +public class CompletionUtil { + static final Key RANGE_TRANSLATION = Key.create("completion.rangeTranslation"); + public static final Key TAIL_TYPE_ATTR = LookupItem.TAIL_TYPE_ATTR; + + private static final CompletionData ourGenericCompletionData = new CompletionData() { + { + final CompletionVariant variant = new CompletionVariant(PsiElement.class, TrueFilter.INSTANCE); + variant.addCompletionFilter(TrueFilter.INSTANCE, TailType.NONE); + registerVariant(variant); + } + }; + private static final HashMap> ourCustomCompletionDatas = new HashMap>(); + + public static final @NonNls String DUMMY_IDENTIFIER = CompletionInitializationContext.DUMMY_IDENTIFIER; + public static final @NonNls String DUMMY_IDENTIFIER_TRIMMED = DUMMY_IDENTIFIER.trim(); + + public static boolean startsWith(String text, String prefix) { + //if (text.length() <= prefix.length()) return false; + return toLowerCase(text).startsWith(toLowerCase(prefix)); + } + + private static String toLowerCase(String text) { + CodeInsightSettings settings = CodeInsightSettings.getInstance(); + switch (settings.COMPLETION_CASE_SENSITIVE) { + case CodeInsightSettings.NONE: + return text.toLowerCase(); + + case CodeInsightSettings.FIRST_LETTER: { + StringBuffer buffer = new StringBuffer(); + buffer.append(text.toLowerCase()); + if (buffer.length() > 0) { + buffer.setCharAt(0, text.charAt(0)); + } + return buffer.toString(); + } + + default: + return text; + } + } + + @Nullable + public static CompletionData getCompletionDataByElement(@Nullable final PsiElement position, @NotNull PsiFile originalFile) { + if (position == null) return null; + + final FileType fileType = position.getParent().getLanguage().getAssociatedFileType(); + if (fileType != null) { + final CompletionData mainData = getCompletionDataByFileType(fileType); + if (mainData != null) { + return mainData; + } + } + + final CompletionData mainData = getCompletionDataByFileType(originalFile.getFileType()); + return mainData != null ? mainData : ourGenericCompletionData; + } + + /** @see CompletionDataEP */ + @Deprecated + public static void registerCompletionData(FileType fileType, NotNullLazyValue completionData) { + ourCustomCompletionDatas.put(fileType, completionData); + } + + /** @see CompletionDataEP */ + @Deprecated + public static void registerCompletionData(FileType fileType, final CompletionData completionData) { + registerCompletionData(fileType, new NotNullLazyValue() { + @NotNull + protected CompletionData compute() { + return completionData; + } + }); + } + + @Nullable + public static CompletionData getCompletionDataByFileType(FileType fileType) { + for(CompletionDataEP ep: Extensions.getExtensions(CompletionDataEP.EP_NAME)) { + if (ep.fileType.equals(fileType.getName())) { + return ep.getHandler(); + } + } + final NotNullLazyValue lazyValue = ourCustomCompletionDatas.get(fileType); + return lazyValue == null ? null : lazyValue.getValue(); + } + + + public static boolean shouldShowFeature(final CompletionParameters parameters, @NonNls final String id) { + if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(id, parameters.getPosition().getProject())) { + FeatureUsageTracker.getInstance().triggerFeatureShown(id); + return true; + } + return false; + } + + public static String findJavaIdentifierPrefix(CompletionParameters parameters) { + return findJavaIdentifierPrefix(parameters.getPosition(), parameters.getOffset()); + } + + public static String findJavaIdentifierPrefix(final PsiElement insertedElement, final int offset) { + return findIdentifierPrefix(insertedElement, offset, character().javaIdentifierPart(), character().javaIdentifierStart()); + } + + public static String findReferenceOrAlphanumericPrefix(CompletionParameters parameters) { + String prefix = findReferencePrefix(parameters); + return prefix == null ? findAlphanumericPrefix(parameters) : prefix; + } + + public static String findAlphanumericPrefix(CompletionParameters parameters) { + return findIdentifierPrefix(parameters.getPosition().getContainingFile(), parameters.getOffset(), character().letterOrDigit(), character().letterOrDigit()); + } + + public static String findIdentifierPrefix(PsiElement insertedElement, int offset, ElementPattern idPart, + ElementPattern idStart) { + if(insertedElement == null) return ""; + final String text = insertedElement.getText(); + + final int offsetInElement = offset - insertedElement.getTextRange().getStartOffset(); + int start = offsetInElement - 1; + while (start >=0 ) { + if (!idPart.accepts(text.charAt(start))) break; + --start; + } + while (start + 1 < offsetInElement && !idStart.accepts(text.charAt(start + 1))) { + start++; + } + + return text.substring(start + 1, offsetInElement).trim(); + } + + @Nullable + public static String findReferencePrefix(CompletionParameters parameters) { + return CompletionData.getReferencePrefix(parameters.getPosition(), parameters.getOffset()); + } + + + static InsertionContext emulateInsertion(InsertionContext oldContext, int newStart, final LookupElement item) { + final InsertionContext newContext = newContext(oldContext, item); + emulateInsertion(item, newStart, newContext); + return newContext; + } + + private static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement) { + final Editor editor = oldContext.getEditor(); + return new InsertionContext(new OffsetMap(editor.getDocument()), Lookup.AUTO_INSERT_SELECT_CHAR, new LookupElement[]{forElement}, oldContext.getFile(), editor, + oldContext.shouldAddCompletionChar()); + } + + public static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement, int startOffset, int tailOffset) { + final InsertionContext context = newContext(oldContext, forElement); + setOffsets(context, startOffset, tailOffset); + return context; + } + + public static void emulateInsertion(LookupElement item, int offset, InsertionContext context) { + setOffsets(context, offset, offset); + + final Editor editor = context.getEditor(); + final Document document = editor.getDocument(); + final String lookupString = item.getLookupString(); + + document.insertString(offset, lookupString); + editor.getCaretModel().moveToOffset(context.getTailOffset()); + PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); + item.handleInsert(context); + } + + private static void setOffsets(InsertionContext context, int offset, final int tailOffset) { + final OffsetMap offsetMap = context.getOffsetMap(); + offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset); + offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, tailOffset); + offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, tailOffset); + context.setTailOffset(tailOffset); + } + + @Nullable + public static PsiElement getTargetElement(LookupElement lookupElement) { + PsiElement psiElement = lookupElement.getPsiElement(); + if (psiElement != null) { + return getOriginalElement(psiElement); + } + + Object object = lookupElement.getObject(); + if (object instanceof LookupValueWithPsiElement) { + final PsiElement element = ((LookupValueWithPsiElement)object).getElement(); + if (element != null) return getOriginalElement(element); + } + + return null; + } + + @Nullable + public static T getOriginalElement(@NotNull T psi) { + final PsiFile file = psi.getContainingFile(); + if (file != null && file != file.getOriginalFile() && psi.getTextRange() != null) { + TextRange range = psi.getTextRange(); + Integer start = range.getStartOffset(); + Integer end = range.getEndOffset(); + final Document document = file.getViewProvider().getDocument(); + if (document != null) { + final OffsetTranslator translator = document.getUserData(RANGE_TRANSLATION); + if (translator != null) { + start = translator.translateOffset(start); + end = translator.translateOffset(end); + if (start == null || end == null) { + return null; + } + } + } + //noinspection unchecked + return (T)PsiTreeUtil.findElementOfClassAtRange(file.getOriginalFile(), start, end, psi.getClass()); + } + + return psi; + } + + @NotNull + public static T getOriginalOrSelf(@NotNull T psi) { + final T element = getOriginalElement(psi); + return element == null ? psi : element; + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/LookupElementProximityWeigher.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/LookupElementProximityWeigher.java index 475349db7641..8dc9f80627ab 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/LookupElementProximityWeigher.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/LookupElementProximityWeigher.java @@ -31,8 +31,7 @@ public class LookupElementProximityWeigher extends CompletionWeigher { return PsiProximityComparator.getProximity(new NullableComputable() { @Override public PsiElement compute() { - Object object = item.getObject(); - return object instanceof PsiElement ? (PsiElement)object : null; + return item.getPsiElement(); } }, location.getCompletionParameters().getPosition(), location.getProcessingContext()); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/DefaultLookupItemRenderer.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/DefaultLookupItemRenderer.java index bd6a7f45aed7..33e0345e7d2a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/DefaultLookupItemRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/DefaultLookupItemRenderer.java @@ -73,14 +73,9 @@ public class DefaultLookupItemRenderer extends LookupElementRenderer return ((Iconable)o).getIcon(Iconable.ICON_FLAG_VISIBILITY); } - if (o instanceof LookupValueWithPsiElement) { - o = ((LookupValueWithPsiElement)o).getElement(); - } - if (o instanceof PsiElement) { - final PsiElement element = (PsiElement)o; - if (element.isValid()) { - return element.getIcon(Iconable.ICON_FLAG_VISIBILITY); - } + final PsiElement element = item.getPsiElement(); + if (element != null && element.isValid()) { + return element.getIcon(Iconable.ICON_FLAG_VISIBILITY); } return null; } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GrCompletionWithLibraryTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GrCompletionWithLibraryTest.groovy index c9b8bbd91651..f1f8060200e9 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GrCompletionWithLibraryTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GrCompletionWithLibraryTest.groovy @@ -78,7 +78,7 @@ class GrCompletionWithLibraryTest extends GroovyCompletionTestBase { public void testEachMethodForRanges() throws Throwable {doBasicTest();} public void testEachMethodForEnumRanges() throws Throwable {doBasicTest();} - public void testPrintlnSpace() { checkCompletion 'print', ' ', "print " } + public void testPrintlnSpace() { checkCompletion 'print', 'l ', "println " } public void testHashCodeSpace() { checkCompletion 'if ("".h', ' ', 'if ("".hashCode() ' } public void testTwoMethodWithSameName() { From 542da31dd53e215e83ac3377d1d718f81f2bb093 Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Sun, 22 Jul 2012 19:25:35 +0400 Subject: [PATCH 21/40] Renderer --- .../AndroidDesignerEditorPanel.java | 5 +- .../designer/designSurface/RootView.java | 19 ++----- .../designer/designSurface/RootView.java | 52 +++++++++++++++++++ 3 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/RootView.java diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java index 692677091645..b99b0b9d602d 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java @@ -32,6 +32,7 @@ import com.intellij.designer.designSurface.selection.NonResizeSelectionDecorator import com.intellij.designer.designSurface.tools.ComponentCreationFactory; import com.intellij.designer.designSurface.tools.ComponentPasteFactory; import com.intellij.designer.model.RadComponent; +import com.intellij.designer.model.RadVisualComponent; import com.intellij.designer.model.WrapInProvider; import com.intellij.designer.palette.DefaultPaletteItem; import com.intellij.designer.palette.PaletteGroup; @@ -178,7 +179,7 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { createRenderer(parser.getLayoutXmlText(), new MyThrowable(), new ThrowableConsumer() { @Override public void consume(RenderSession session) throws Throwable { - RootView rootView = new RootView(session.getImage(), 30, 20); + RootView rootView = new RootView(30, 20, session.getImage()); try { parser.updateRootComponent(session, rootView); } @@ -360,7 +361,7 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { private void removeNativeRoot() { if (myRootComponent != null) { - Component component = ((RadViewComponent)myRootComponent).getNativeComponent(); + Component component = ((RadVisualComponent)myRootComponent).getNativeComponent(); if (component != null) { myLayeredPane.remove(component.getParent()); } diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/RootView.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/RootView.java index d572e255e985..bda836ac10fd 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/designSurface/RootView.java +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/RootView.java @@ -24,26 +24,16 @@ import java.util.List; /** * @author Alexander Lobas */ -public class RootView extends JComponent { - private int myX; - private int myY; - private BufferedImage myImage; +public class RootView extends com.intellij.designer.designSurface.RootView { private List myEmptyRegions; - public RootView(BufferedImage image, int x, int y) { - myX = x; - myY = y; - setImage(image); - } - - public BufferedImage getImage() { - return myImage; + public RootView(int x, int y, BufferedImage image) { + super(x, y, image); } public void setImage(BufferedImage image) { - myImage = image; + super.setImage(image); myEmptyRegions = new ArrayList(); - setBounds(myX, myY, image.getWidth(), image.getHeight()); } public void addEmptyRegion(int x, int y, int width, int height) { @@ -61,7 +51,6 @@ public class RootView extends JComponent { @Override public void paintComponent(Graphics g) { super.paintComponent(g); - g.drawImage(myImage, 0, 0, null); for (EmptyRegion r : myEmptyRegions) { g.setColor(r.myColor); diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/RootView.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/RootView.java new file mode 100644 index 000000000000..f649ab17e897 --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/RootView.java @@ -0,0 +1,52 @@ +/* + * 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.designer.designSurface; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Alexander Lobas + */ +public class RootView extends JComponent { + private int myX; + private int myY; + protected BufferedImage myImage; + + public RootView(int x, int y, BufferedImage image) { + myX = x; + myY = y; + setImage(image); + } + + public BufferedImage getImage() { + return myImage; + } + + public void setImage(BufferedImage image) { + myImage = image; + setBounds(myX, myY, image.getWidth(), image.getHeight()); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + g.drawImage(myImage, 0, 0, null); + } +} \ No newline at end of file From 1c04633deb39ae0a56373edcdf479d6481cd959d Mon Sep 17 00:00:00 2001 From: nik Date: Mon, 23 Jul 2012 09:38:48 +0400 Subject: [PATCH 22/40] tests fixed --- .../openapi/roots/ui/configuration/ModulesConfigurator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java index b81480bb6c8f..b85b7b1b5ecd 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModulesConfigurator.java @@ -163,7 +163,6 @@ public class ModulesConfigurator implements ModulesProvider, ModuleEditor.Change } }; - myModuleEditors.add(moduleEditor); myModuleEditors.add(moduleEditor); moduleEditor.addChangeListener(this); From ddccd5fb72b8cabee2477ed43bc490b59aa30604 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 23 Jul 2012 11:38:45 +0200 Subject: [PATCH 23/40] Make test more suitable for Windows --- .../intellij/openapi/util/io/FileAttributesReadingTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java index 414ed48fcd15..56bdc6fc4147 100644 --- a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java @@ -80,8 +80,8 @@ public class FileAttributesReadingTest { public void linkToFile() throws Exception { final File file = FileUtil.createTempFile(myTempDirectory, "test.", ".txt", true); FileUtil.writeToFile(file, myTestData); - assertTrue(file.setWritable(false, false)); assertTrue(file.setLastModified(file.lastModified() - 5000)); + assertTrue(file.setWritable(false, false)); final File link = IoTestUtil.createTempLink(file.getPath(), new File(myTempDirectory, "link").getPath()); final FileAttributes attributes = getAttributes(link); @@ -98,8 +98,8 @@ public class FileAttributesReadingTest { public void doubleLink() throws Exception { final File file = FileUtil.createTempFile(myTempDirectory, "test.", ".txt", true); FileUtil.writeToFile(file, myTestData); - assertTrue(file.setWritable(false, false)); assertTrue(file.setLastModified(file.lastModified() - 5000)); + assertTrue(file.setWritable(false, false)); final File link1 = IoTestUtil.createTempLink(file.getPath(), new File(myTempDirectory, "link1").getPath()); final File link2 = IoTestUtil.createTempLink(link1.getPath(), new File(myTempDirectory, "link2").getPath()); From 5982b9f4401c21c7fb8f30f93307f1e7797d96fd Mon Sep 17 00:00:00 2001 From: anna Date: Mon, 23 Jul 2012 11:54:22 +0200 Subject: [PATCH 24/40] allow single imports of methods with the same name (IDEA-89144) --- .../intention/impl/AddSingleMemberStaticImportAction.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddSingleMemberStaticImportAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddSingleMemberStaticImportAction.java index 694310f10a82..7976fa1b5b62 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddSingleMemberStaticImportAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddSingleMemberStaticImportAction.java @@ -78,7 +78,12 @@ public class AddSingleMemberStaticImportAction extends PsiElementBaseIntentionAc } } } - if (importList.findSingleImportStatement(refExpr.getReferenceName()) == null) { + final PsiImportStatementBase importStatement = importList.findSingleImportStatement(refExpr.getReferenceName()); + if (importStatement == null) { + return qName; + } + final PsiElement resolve = importStatement.resolve(); + if (resolve instanceof PsiMember && ((PsiMember)resolve).getContainingClass() != aClass) { return qName; } } From 09cafea14218cb1eebb433a482ddb0c434d0c27a Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Fri, 20 Jul 2012 20:42:13 +0400 Subject: [PATCH 25/40] android dependency processor: search for refs to R class instead of usages of each constant field (inside project they may be non-existent or non-up-to date) --- .../compiler/DependencyProcessor.java | 4 +- .../compiler/make/DependencyCache.java | 2 +- .../android/util/AndroidCommonUtils.java | 2 +- plugins/android/src/META-INF/plugin.xml | 1 + .../compiler/AndroidDependencyProcessor.java | 88 +++++++++++++++++++ 5 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 plugins/android/src/org/jetbrains/android/compiler/AndroidDependencyProcessor.java diff --git a/java/compiler/impl/src/com/intellij/compiler/DependencyProcessor.java b/java/compiler/impl/src/com/intellij/compiler/DependencyProcessor.java index dbf3703c2337..3907eb81c040 100644 --- a/java/compiler/impl/src/com/intellij/compiler/DependencyProcessor.java +++ b/java/compiler/impl/src/com/intellij/compiler/DependencyProcessor.java @@ -15,6 +15,8 @@ */ package com.intellij.compiler; +import com.intellij.compiler.make.CacheCorruptedException; +import com.intellij.compiler.make.CachingSearcher; import com.intellij.openapi.compiler.CompileContext; import com.intellij.openapi.extensions.ExtensionPointName; @@ -25,5 +27,5 @@ import com.intellij.openapi.extensions.ExtensionPointName; public interface DependencyProcessor { ExtensionPointName EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.compiler.makeDependencyProcessor"); - void processDependencies(CompileContext context, int classQualifiedName); + void processDependencies(CompileContext context, int classQualifiedName, CachingSearcher searcher) throws CacheCorruptedException; } diff --git a/java/compiler/impl/src/com/intellij/compiler/make/DependencyCache.java b/java/compiler/impl/src/com/intellij/compiler/make/DependencyCache.java index dab0f311254e..92f3285bf8ef 100644 --- a/java/compiler/impl/src/com/intellij/compiler/make/DependencyCache.java +++ b/java/compiler/impl/src/com/intellij/compiler/make/DependencyCache.java @@ -429,7 +429,7 @@ public class DependencyCache { } changedRetentionPolicyDependencyProcessor.checkAnnotationRetentionPolicyChanges(qName); for (DependencyProcessor additionalProcessor : DependencyProcessor.EXTENSION_POINT_NAME.getExtensions()) { - additionalProcessor.processDependencies(context, qName); + additionalProcessor.processDependencies(context, qName, searcher); } } else { diff --git a/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java b/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java index 4bf3ff099498..3be8392b3e29 100644 --- a/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java +++ b/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java @@ -61,7 +61,7 @@ public class AndroidCommonUtils { private static final Pattern ERROR_PATTERN = Pattern.compile(".*error.*"); private static final Pattern EXCEPTION_PATTERN = Pattern.compile(".*exception.*"); - private static final Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class"); + public static final Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class"); private static final Pattern MANIFEST_PATTERN = Pattern.compile("Manifest(\\$.*)?\\.class"); private static final String BUILD_CONFIG_CLASS_NAME = "BuildConfig.class"; diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index dfade9b013a2..a08133a02f03 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -143,6 +143,7 @@ + diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidDependencyProcessor.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidDependencyProcessor.java new file mode 100644 index 000000000000..7423d5fdf7a0 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidDependencyProcessor.java @@ -0,0 +1,88 @@ +package org.jetbrains.android.compiler; + +import com.intellij.compiler.DependencyProcessor; +import com.intellij.compiler.make.*; +import com.intellij.facet.ProjectFacetManager; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.ex.CompileContextEx; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.containers.HashSet; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidCommonUtils; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.Collection; +import java.util.Set; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidDependencyProcessor implements DependencyProcessor { + @Override + public void processDependencies(final CompileContext context, int classQualifiedName, final CachingSearcher searcher) + throws CacheCorruptedException { + if (!(context instanceof CompileContextEx)) { + return; + } + final Project project = context.getProject(); + + if (!ProjectFacetManager.getInstance(project).hasFacets(AndroidFacet.ID)) { + return; + } + final DependencyCache depCache = ((CompileContextEx)context).getDependencyCache(); + final Cache cache = depCache.getCache(); + final String path = cache.getPath(classQualifiedName); + final String classFileName = new File(path).getName(); + + if (!AndroidCommonUtils.R_PATTERN.matcher(classFileName).matches()) { + return; + } + final String qName = depCache.resolve(classQualifiedName); + final int idx = qName.indexOf('$'); + final String topLevelClassName = idx < 0 ? qName : qName.substring(0, idx); + final Set qNamesToMark = new HashSet(); + + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + final PsiClass[] classes = + JavaPsiFacade.getInstance(project).findClasses(topLevelClassName, GlobalSearchScope.allScope(project)); + + for (PsiClass aClass : classes) { + final Collection references = searcher.findReferences(aClass, true); + + for (PsiReference reference : references) { + final PsiClass ownerClass = getOwnerClass(reference.getElement()); + if (ownerClass != null && !ownerClass.equals(aClass)) { + final String ownerClassQName = ownerClass.getQualifiedName(); + + if (ownerClassQName != null) { + qNamesToMark.add(ownerClassQName); + } + } + } + } + } + }); + + for (String toMark : qNamesToMark) { + final int qualifiedName = depCache.getSymbolTable().getId(toMark); + depCache.markClass(qualifiedName); + } + } + + @Nullable + private static PsiClass getOwnerClass(PsiElement element) { + while (!(element instanceof PsiFile)) { + if (element instanceof PsiClass && element.getParent() instanceof PsiJavaFile) { + return (PsiClass)element; + } + element = element.getParent(); + } + return null; + } +} From 8afc4486fac6b4a234496eca71e6020628e8ae97 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 23 Jul 2012 14:36:50 +0400 Subject: [PATCH 26/40] do not resolve/complete android private system resources --- .../propertyTable/editors/ResourceDialog.java | 22 ++-- .../compiler/ResourceNamesValidityState.java | 43 +++---- .../ResourceReferenceConverter.java | 16 +-- .../FileResourceProcessor.java | 11 ++ .../resourceManagers/ResourceManager.java | 114 +++++++++++++----- .../SystemResourceManager.java | 59 +++++++++ plugins/android/testData/dom/animator/hl.xml | 4 +- plugins/android/testData/dom/animator/hl1.xml | 14 +++ .../animator/myInterpolator.xml} | 0 .../android/testData/dom/layout/idref2.xml | 2 +- .../testData/dom/layout/idref2_after.xml | 2 +- .../testData/dom/layout/layoutAttrs2.xml | 2 +- .../dom/layout/privateAndPublicResources.xml | 10 ++ .../data/res/drawable/menuitem_background.xml | 0 .../android-1.5/data/res/drawable/private.xml | 0 .../android/dom/AndroidAnimatorDomTest.java | 14 ++- .../android/dom/AndroidLayoutDomTest.java | 6 +- 17 files changed, 231 insertions(+), 88 deletions(-) create mode 100644 plugins/android/src/org/jetbrains/android/resourceManagers/FileResourceProcessor.java create mode 100644 plugins/android/testData/dom/animator/hl1.xml rename plugins/android/testData/{sdk1.5/platforms/android-1.5/data/res/interpolator/platformInterpolator.xml => dom/animator/myInterpolator.xml} (100%) create mode 100644 plugins/android/testData/dom/layout/privateAndPublicResources.xml create mode 100644 plugins/android/testData/sdk1.5/platforms/android-1.5/data/res/drawable/menuitem_background.xml create mode 100644 plugins/android/testData/sdk1.5/platforms/android-1.5/data/res/drawable/private.xml diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceDialog.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceDialog.java index 464d3b747d8c..5e0e3e9606f9 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceDialog.java +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceDialog.java @@ -51,8 +51,8 @@ import org.jetbrains.android.actions.CreateXmlResourceDialog; import org.jetbrains.android.dom.resources.ResourceElement; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidRootUtil; +import org.jetbrains.android.resourceManagers.FileResourceProcessor; import org.jetbrains.android.resourceManagers.ResourceManager; -import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.Nls; @@ -603,25 +603,23 @@ public class ResourceDialog extends DialogWrapper implements TreeSelectionListen public ResourceGroup(ResourceType type, ResourceManager manager) { myType = type; - String resourceType = type.getName(); + final String resourceType = type.getName(); Collection resourceNames = manager.getValueResourceNames(resourceType); for (String resourceName : resourceNames) { myItems.add(new ResourceItem(this, resourceName, null, RESOURCE_ITEM_ICON)); } + final Set fileNames = new HashSet(); - Set fileNames = new HashSet(); - List dirs = manager.getResourceSubdirs(resourceType); - for (VirtualFile dir : dirs) { - for (VirtualFile resourceFile : dir.getChildren()) { - if (!resourceFile.isDirectory()) { - String fileName = AndroidCommonUtils.getResourceName(resourceType, resourceFile.getName()); - if (fileNames.add(fileName)) { - myItems.add(new ResourceItem(this, fileName, resourceFile, resourceFile.getFileType().getIcon())); - } + manager.processFileResources(resourceType, new FileResourceProcessor() { + @Override + public boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) { + if (fileNames.add(resName)) { + myItems.add(new ResourceItem(ResourceGroup.this, resName, resFile, resFile.getFileType().getIcon())); } + return true; } - } + }); if (type == ResourceType.ID) { for (String id : manager.getIds()) { diff --git a/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java b/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java index a4622edc6683..b7afc0093a49 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java +++ b/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java @@ -4,11 +4,8 @@ import com.android.resources.ResourceType; import com.android.sdklib.IAndroidTarget; import com.intellij.openapi.compiler.ValidityState; import com.intellij.openapi.module.Module; -import com.intellij.openapi.roots.ContentIterator; -import com.intellij.openapi.roots.impl.FileIndexImplUtil; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileFilter; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashMap; import org.jetbrains.android.dom.resources.DeclareStyleable; @@ -16,6 +13,7 @@ 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.FileResourceProcessor; import org.jetbrains.android.resourceManagers.LocalResourceManager; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.util.AndroidCommonUtils; @@ -74,31 +72,24 @@ public class ResourceNamesValidityState implements ValidityState { } } - for (final VirtualFile subdir : manager.getResourceSubdirs(null)) { - final String subdirName = subdir.getName(); - final int index = subdirName.indexOf('-'); - final String typeName = index >= 0 ? subdirName.substring(0, index) : subdirName; - final ResourceType type = ResourceType.getEnum(typeName); - final boolean idProvidingResource = type != null && ArrayUtil.find(AndroidCommonUtils.ID_PROVIDING_RESOURCE_TYPES, type) >= 0; + manager.processFileResources(null, new FileResourceProcessor() { + @Override + public boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) { + final ResourceType type = ResourceType.getEnum(resFolderType); + final boolean idProvidingResource = type != null && ArrayUtil.find(AndroidCommonUtils.ID_PROVIDING_RESOURCE_TYPES, type) >= 0; - FileIndexImplUtil.iterateRecursively(subdir, VirtualFileFilter.ALL, new ContentIterator() { - @Override - public boolean processFile(VirtualFile fileOrDir) { - if (!fileOrDir.isDirectory()) { - ResourceFileData data = myResources.get(fileOrDir.getPath()); - if (data == null) { - data = new ResourceFileData(); - myResources.put(fileOrDir.getPath(), data); - } - - if (idProvidingResource) { - data.setTimestamp(fileOrDir.getTimeStamp()); - } - } - return true; + ResourceFileData data = myResources.get(resFile.getPath()); + if (data == null) { + data = new ResourceFileData(); + myResources.put(resFile.getPath(), data); } - }); - } + + if (idProvidingResource) { + data.setTimestamp(resFile.getTimeStamp()); + } + return true; + } + }); } private static void addValueResources(VirtualFile file, 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 258d5473d370..d6db01d0ae03 100644 --- a/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java +++ b/plugins/android/src/org/jetbrains/android/dom/converters/ResourceReferenceConverter.java @@ -33,9 +33,9 @@ import org.jetbrains.android.dom.resources.ResourceValue; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.inspections.CreateFileResourceQuickFix; import org.jetbrains.android.inspections.CreateValueResourceQuickFix; +import org.jetbrains.android.resourceManagers.FileResourceProcessor; import org.jetbrains.android.resourceManagers.LocalResourceManager; import org.jetbrains.android.resourceManagers.ResourceManager; -import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -164,13 +164,15 @@ public class ResourceReferenceConverter extends ResolvingConverter result = new HashSet(); final LocalResourceManager manager = facet.getLocalResourceManager(); - for (VirtualFile resSubdir : manager.getResourceSubdirs(null)) { - final String resType = AndroidCommonUtils.getResourceTypeByDirName(resSubdir.getName()); - - if (resType != null && ResourceType.getEnum(resType) != null) { - result.add(resType); + manager.processFileResources(null, new FileResourceProcessor() { + @Override + public boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) { + if (ResourceType.getEnum(resFolderType) != null) { + result.add(resFolderType); + } + return true; } - } + }); result.addAll(manager.getValueResourceTypes()); if (manager.getIds().size() > 0) { diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/FileResourceProcessor.java b/plugins/android/src/org/jetbrains/android/resourceManagers/FileResourceProcessor.java new file mode 100644 index 000000000000..4a5484cfb2ac --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/FileResourceProcessor.java @@ -0,0 +1,11 @@ +package org.jetbrains.android.resourceManagers; + +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +/** + * @author Eugene.Kudelevsky + */ +public interface FileResourceProcessor { + boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType); +} diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java index 293c54d4a3c9..7578b7a70707 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java @@ -75,6 +75,30 @@ public abstract class ResourceManager { @Nullable public abstract VirtualFile getResourceDir(); + public boolean processFileResources(@Nullable String resourceType, @NotNull FileResourceProcessor processor) { + return processFileResources(resourceType, processor, true); + } + + public boolean processFileResources(@Nullable String resourceType, @NotNull FileResourceProcessor processor, boolean publicOnly) { + for (VirtualFile resSubdir : getResourceSubdirs(resourceType)) { + final String resType = AndroidCommonUtils.getResourceTypeByDirName(resSubdir.getName()); + + if (resType != null) { + assert resourceType == null || resourceType.equals(resType); + for (VirtualFile resFile : resSubdir.getChildren()) { + final String resName = AndroidCommonUtils.getResourceName(resType, resFile.getName()); + + if (!resFile.isDirectory() && (!publicOnly || isResourcePublic(resType, resName))) { + if (!processor.process(resFile, resName, resType)) { + return false; + } + } + } + } + } + return true; + } + public boolean isResourceDir(@NotNull VirtualFile dir) { return dir.equals(getResourceDir()); } @@ -84,38 +108,44 @@ public abstract class ResourceManager { return VirtualFile.EMPTY_ARRAY; } + protected boolean isResourcePublic(@NotNull String type, @NotNull String name) { + return true; + } + @NotNull public List getResourceSubdirs(@Nullable String resourceType) { return AndroidResourceUtil.getResourceSubdirs(resourceType, getAllResourceDirs()); } @NotNull - public List findResourceFiles(@NotNull String resType, - @Nullable String resName, - boolean distinguishDelimetersInName, - @NotNull String... extensions) { - List result = new ArrayList(); - Set extensionSet = new HashSet(); + public List findResourceFiles(@NotNull final String resType1, + @Nullable final String resName1, + final boolean distinguishDelimetersInName, + @NotNull final String... extensions) { + final List result = new ArrayList(); + final Set extensionSet = new HashSet(); addAll(extensionSet, extensions); - for (VirtualFile dir : getResourceSubdirs(resType)) { - for (final VirtualFile resFile : dir.getChildren()) { - String extension = resFile.getExtension(); - if (extensions.length == 0 || extensionSet.contains(extension)) { - String s = AndroidCommonUtils.getResourceName(resType, resFile.getName()); - if (resName == null || AndroidUtils.equal(resName, s, distinguishDelimetersInName)) { - PsiFile file = ApplicationManager.getApplication().runReadAction(new Computable() { - @Nullable - public PsiFile compute() { - return PsiManager.getInstance(myModule.getProject()).findFile(resFile); - } - }); - if (file != null) { - result.add(file); + + processFileResources(resType1, new FileResourceProcessor() { + @Override + public boolean process(@NotNull final VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) { + final String extension = resFile.getExtension(); + + if ((extensions.length == 0 || extensionSet.contains(extension)) && + (resName1 == null || AndroidUtils.equal(resName1, resName, distinguishDelimetersInName))) { + final PsiFile file = ApplicationManager.getApplication().runReadAction(new Computable() { + @Nullable + public PsiFile compute() { + return PsiManager.getInstance(myModule.getProject()).findFile(resFile); } + }); + if (file != null) { + result.add(file); } } + return true; } - } + }); return result; } @@ -171,7 +201,14 @@ public abstract class ResourceManager { if (!resources.isValid() || myModule.isDisposed() || myModule.getProject().isDisposed()) { return; } - result.addAll(AndroidResourceUtil.getValueResourcesFromElement(resourceType, resources)); + final List valueResources = AndroidResourceUtil.getValueResourcesFromElement(resourceType, resources); + for (ResourceElement valueResource : valueResources) { + final String resName = valueResource.getName().getValue(); + + if (resName != null && isResourcePublic(resourceType, resName)) { + result.add(valueResource); + } + } } }); } @@ -207,15 +244,16 @@ public abstract class ResourceManager { } @NotNull - public Set getFileResourcesNames(@NotNull String resourceType) { - Set result = new HashSet(); - List dirs = getResourceSubdirs(resourceType); - for (VirtualFile dir : dirs) { - for (VirtualFile resourceFile : dir.getChildren()) { - if (resourceFile.isDirectory()) continue; - result.add(AndroidCommonUtils.getResourceName(resourceType, resourceFile.getName())); + public Set getFileResourcesNames(@NotNull final String resourceType) { + final Set result = new HashSet(); + + processFileResources(resourceType, new FileResourceProcessor() { + @Override + public boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) { + result.add(resName); + return true; } - } + }); return result; } @@ -269,7 +307,9 @@ public abstract class ResourceManager { if (entries != null) { for (ResourceEntry entry : entries) { - result.add(entry); + if (isResourcePublic(entry.getType(), entry.getName())) { + result.add(entry); + } } } } @@ -293,6 +333,10 @@ public abstract class ResourceManager { // searches only declarations such as "@+id/..." @Nullable public List findIdDeclarations(@NotNull final String id) { + if (!isResourcePublic(ResourceType.ID.getName(), id)) { + return Collections.emptyList(); + } + final List declarations = new ArrayList(); final Collection files = FileBasedIndex.getInstance().getContainingFiles(AndroidIdIndex.INDEX_ID, id, GlobalSearchScope.allScope(myModule.getProject())); @@ -357,9 +401,13 @@ public abstract class ResourceManager { for (VirtualFile resSubdir : getResourceSubdirsToSearchIds()) { for (VirtualFile resFile : resSubdir.getChildren()) { final Set ids = file2ids.get(resFile); - + if (ids != null) { - result.addAll(ids); + for (String id : ids) { + if (isResourcePublic(ResourceType.ID.getName(), id)) { + result.add(id); + } + } } } } diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java index 28b395370a71..758fe234c1c9 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/SystemResourceManager.java @@ -16,25 +16,84 @@ package org.jetbrains.android.resourceManagers; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.SdkConstants; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiField; +import com.intellij.util.containers.HashMap; +import com.intellij.util.containers.HashSet; import com.intellij.util.xml.ConvertContext; import org.jetbrains.android.dom.attrs.AttributeDefinitions; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.sdk.AndroidTargetData; +import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; +import java.util.Set; + /** * @author coyote */ public class SystemResourceManager extends ResourceManager { private final AndroidPlatform myPlatform; + private final Map> myPublicResourcesCache = new HashMap>(); public SystemResourceManager(@NotNull AndroidFacet facet, @NotNull AndroidPlatform androidPlatform) { super(facet); myPlatform = androidPlatform; + final Module module = facet.getModule(); + + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + buildPublicResourceCache(module); + } + }); + } + + private void buildPublicResourceCache(@NotNull Module module) { + final PsiClass rClass = JavaPsiFacade.getInstance(module.getProject()) + .findClass(SdkConstants.CLASS_R, module.getModuleWithDependenciesAndLibrariesScope(false)); + if (rClass == null) { + return; + } + + for (PsiClass resourceClass : rClass.getInnerClasses()) { + final String resType = resourceClass.getName(); + + if (resType != null) { + Set resNameSet = myPublicResourcesCache.get(resType); + + if (resNameSet == null) { + resNameSet = new HashSet(); + myPublicResourcesCache.put(resType, resNameSet); + } + + for (PsiField resField : resourceClass.getFields()) { + final String resName = resField.getName(); + + if (resName != null) { + resNameSet.add(resName); + } + } + } + } + } + + protected boolean isResourcePublic(@NotNull String type, @NotNull String name) { + final Set fieldNames = myPublicResourcesCache.get(type); + if (fieldNames == null || fieldNames.isEmpty()) { + return false; + } + final String fieldName = AndroidResourceUtil.getFieldNameByResourceName(name); + return fieldNames.contains(fieldName); } @NotNull diff --git a/plugins/android/testData/dom/animator/hl.xml b/plugins/android/testData/dom/animator/hl.xml index 6be71be8139f..f1d79d2a300e 100644 --- a/plugins/android/testData/dom/animator/hl.xml +++ b/plugins/android/testData/dom/animator/hl.xml @@ -1,12 +1,12 @@ + + + diff --git a/plugins/android/testData/sdk1.5/platforms/android-1.5/data/res/interpolator/platformInterpolator.xml b/plugins/android/testData/dom/animator/myInterpolator.xml similarity index 100% rename from plugins/android/testData/sdk1.5/platforms/android-1.5/data/res/interpolator/platformInterpolator.xml rename to plugins/android/testData/dom/animator/myInterpolator.xml diff --git a/plugins/android/testData/dom/layout/idref2.xml b/plugins/android/testData/dom/layout/idref2.xml index d2bae0c005c2..3cb1b66380ca 100644 --- a/plugins/android/testData/dom/layout/idref2.xml +++ b/plugins/android/testData/dom/layout/idref2.xml @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/idref2_after.xml b/plugins/android/testData/dom/layout/idref2_after.xml index 58e6e0de9700..27801ff675f2 100644 --- a/plugins/android/testData/dom/layout/idref2_after.xml +++ b/plugins/android/testData/dom/layout/idref2_after.xml @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/layoutAttrs2.xml b/plugins/android/testData/dom/layout/layoutAttrs2.xml index 22f8ff980477..3cdd0013cde0 100644 --- a/plugins/android/testData/dom/layout/layoutAttrs2.xml +++ b/plugins/android/testData/dom/layout/layoutAttrs2.xml @@ -1,3 +1,3 @@ + style="@android:style/MediaButton"> \ No newline at end of file diff --git a/plugins/android/testData/dom/layout/privateAndPublicResources.xml b/plugins/android/testData/dom/layout/privateAndPublicResources.xml new file mode 100644 index 000000000000..77c1fbb8cf33 --- /dev/null +++ b/plugins/android/testData/dom/layout/privateAndPublicResources.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/plugins/android/testData/sdk1.5/platforms/android-1.5/data/res/drawable/menuitem_background.xml b/plugins/android/testData/sdk1.5/platforms/android-1.5/data/res/drawable/menuitem_background.xml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/plugins/android/testData/sdk1.5/platforms/android-1.5/data/res/drawable/private.xml b/plugins/android/testData/sdk1.5/platforms/android-1.5/data/res/drawable/private.xml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidAnimatorDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidAnimatorDomTest.java index 05ceb48d4432..3602bc3d8750 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidAnimatorDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidAnimatorDomTest.java @@ -42,15 +42,21 @@ public class AndroidAnimatorDomTest extends AndroidDomTest { toTestCompletion("root.xml", "root_after.xml"); } - public void testHighlighting() throws Throwable { - doTestHighlighting("hl.xml"); - } - public void testTagNames() throws Throwable { CamelHumpMatcher.forceStartMatching(getTestRootDisposable()); toTestCompletion("tn.xml", "tn_after.xml"); } + // todo: provide normal mock android jar and add public interpolator into it + /*public void testHighlighting() throws Throwable { + doTestHighlighting("hl.xml"); + }*/ + + public void testHighlighting1() throws Throwable { + copyFileToProject("myInterpolator.xml", "res/interpolator/myInterpolator.xml"); + doTestHighlighting("hl1.xml"); + } + public void testAttributeNames() throws Throwable { toTestCompletion("an1.xml", "an1_after.xml"); toTestCompletion("an2.xml", "an2_after.xml"); diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java index 9b6c2440d6e3..b1eeb1a7c490 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidLayoutDomTest.java @@ -334,7 +334,7 @@ public class AndroidLayoutDomTest extends AndroidDomTest { } public void testIdCompletion2() throws Throwable { - doTestCompletionVariants("idcompl2.xml", "@android:id/text", "@android:id/text1", "@android:id/text2"); + doTestCompletionVariants("idcompl2.xml", "@android:id/text1", "@android:id/text2"); } public void testIdHighlighting() throws Throwable { @@ -599,6 +599,10 @@ public class AndroidLayoutDomTest extends AndroidDomTest { myFixture.checkResultByFile("res/drawable/unknown.xml", testFolder + '/' + getTestName(true) + "_drawable_after.xml", true); } + public void testPrivateAndPublicResources() throws Throwable { + doTestHighlighting(); + } + private void doCreateFileResourceFromUsage(VirtualFile virtualFile) { myFixture.configureFromExistingVirtualFile(virtualFile); final List infos = myFixture.doHighlighting(); From 3e5f3a7780998e2553ba4765df61bd8c706baf2b Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 23 Jul 2012 14:52:09 +0400 Subject: [PATCH 27/40] fix test --- .../testData/dom/manifest/ActivityCompletion3_after.xml | 7 ------- .../org/jetbrains/android/dom/AndroidManifestDomTest.java | 3 +-- 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 plugins/android/testData/dom/manifest/ActivityCompletion3_after.xml diff --git a/plugins/android/testData/dom/manifest/ActivityCompletion3_after.xml b/plugins/android/testData/dom/manifest/ActivityCompletion3_after.xml deleted file mode 100644 index 2d7857cf2f3a..000000000000 --- a/plugins/android/testData/dom/manifest/ActivityCompletion3_after.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java index 3bcc72b161d1..90b96258e668 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java @@ -117,10 +117,9 @@ public class AndroidManifestDomTest extends AndroidDomTest { } public void testActivityCompletion3() throws Throwable { - CamelHumpMatcher.forceStartMatching(getTestRootDisposable()); copyFileToProject("MyActivity.java", "src/p1/p2/MyActivity.java"); copyFileToProject("MyActivity1.java", "src/p1/p2/p3/MyActivity1.java"); - doTestCompletion(false); + doTestCompletionVariants(getTestName(false) + ".xml", ".MyActivity", ".p3.MyActivity1"); } public void testActivityCompletion4() throws Throwable { From a8578fada7a60475b5a50eb342c4515c759db7f7 Mon Sep 17 00:00:00 2001 From: Sergey Evdokimov Date: Mon, 23 Jul 2012 14:06:50 +0400 Subject: [PATCH 28/40] IDEA-89080 (Unable to open a maven project) --- .../org/jetbrains/idea/maven/utils/MavenJDOMUtil.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenJDOMUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenJDOMUtil.java index e82de8a68d04..a259d9663996 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenJDOMUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenJDOMUtil.java @@ -25,6 +25,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.impl.source.parsing.xml.XmlBuilder; import com.intellij.psi.impl.source.parsing.xml.XmlBuilderDriver; import org.jdom.Element; +import org.jdom.IllegalNameException; import org.jetbrains.annotations.Nullable; import java.io.IOException; @@ -76,7 +77,13 @@ public class MavenJDOMUtil { String name = localName.toString(); if (StringUtil.isEmptyOrSpaces(name)) return ProcessingOrder.TAGS; - Element newElement = new Element(name); + Element newElement; + try { + newElement = new Element(name); + } + catch (IllegalNameException e) { + newElement = new Element("invalidName"); + } Element parent = stack.isEmpty() ? null : stack.getLast(); if (parent == null) { From 948233e1bd5f5513666afdb11eca1ed354a9b036 Mon Sep 17 00:00:00 2001 From: Sergey Evdokimov Date: Mon, 23 Jul 2012 14:18:47 +0400 Subject: [PATCH 29/40] Optimization of parsing XML --- .../idea/maven/utils/MavenJDOMUtil.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenJDOMUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenJDOMUtil.java index a259d9663996..9eb88b27f1b5 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenJDOMUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenJDOMUtil.java @@ -29,10 +29,7 @@ import org.jdom.IllegalNameException; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; +import java.util.*; public class MavenJDOMUtil { @Nullable @@ -101,17 +98,14 @@ public class MavenJDOMUtil { String name = localName.toString(); if (StringUtil.isEmptyOrSpaces(name)) return; - int index = -1; - for (int i = stack.size() - 1; i >= 0; i--) { - if (stack.get(i).getName().equals(name)) { - index = i; + for (Iterator itr = stack.descendingIterator(); itr.hasNext(); ) { + Element element = itr.next(); + + if (element.getName().equals(name)) { + while (stack.removeLast() != element) {} break; } } - if (index == -1) return; - while (stack.size() > index) { - stack.removeLast(); - } } public void textElement(CharSequence text, CharSequence physical, int startoffset, int endoffset) { From 89c7aa083d29ad1d8079054e02f6365235c0a1ce Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 23 Jul 2012 13:21:22 +0200 Subject: [PATCH 30/40] Make test more suitable for Windows --- .../openapi/util/io/FileSystemUtil.java | 67 +++++++++++++------ .../io/FileAttributesNio2ReadingTest.java | 2 + .../util/io/FileAttributesReadingTest.java | 11 ++- 3 files changed, 59 insertions(+), 21 deletions(-) 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 b22a09e22d07..e38a99e630a1 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java @@ -44,6 +44,19 @@ public class FileSystemUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.io.FileSystemUtil"); + private interface Mediator { + @Nullable + FileAttributes getAttributes(@NotNull String path) throws Exception; + + @Nullable + String resolveSymLink(@NotNull String path) throws Exception; + + void setPermissions(@NotNull String path, int permissions) throws Exception; + + @NotNull + String getName(); + } + @NotNull private static Mediator ourMediator = getMediator(); @@ -51,16 +64,16 @@ public class FileSystemUtil { final boolean forceUseNio2 = SystemProperties.getBooleanProperty(FORCE_USE_NIO2_KEY, false); final String quickTestPath = SystemInfo.isWindows ? "C:\\" : "/"; - // todo: move IdeaWin32 interface to this package, add mediator - - if (!forceUseNio2 && (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris || SystemInfo.isFreeBSD)) { - try { - final Mediator mediator = new JnaUnixMediatorImpl(); - mediator.getAttributes(quickTestPath); - return mediator; - } - catch (Throwable t) { - LOG.warn(t); + if (!forceUseNio2) { + if (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris || SystemInfo.isFreeBSD) { + try { + final Mediator mediator = new JnaUnixMediatorImpl(); + mediator.getAttributes(quickTestPath); + return mediator; + } + catch (Throwable t) { + LOG.warn(t); + } } } @@ -84,6 +97,11 @@ public class FileSystemUtil { ourMediator = getMediator(); } + @TestOnly + static String getMediatorName() { + return ourMediator.getName(); + } + private FileSystemUtil() { } @Nullable @@ -161,15 +179,6 @@ public class FileSystemUtil { setPermissions(file.getAbsolutePath(), permissions); } - private interface Mediator { - @Nullable - FileAttributes getAttributes(@NotNull String path) throws Exception; - - @Nullable - String resolveSymLink(@NotNull String path) throws Exception; - - void setPermissions(@NotNull String path, int permissions) throws Exception; - } // todo[r.sh] remove reflection after migration to JDK 7 @SuppressWarnings("OctalInteger") @@ -267,6 +276,12 @@ public class FileSystemUtil { } } + @NotNull + @Override + public String getName() { + return "NIO2"; + } + private static final Map ATTRIBUTES_MAP; static { ATTRIBUTES_MAP = new HashMap(); @@ -314,6 +329,7 @@ public class FileSystemUtil { } } + // thanks to SVNKit for the idea private static class JnaUnixMediatorImpl implements Mediator { @SuppressWarnings("OctalInteger") @@ -390,8 +406,15 @@ public class FileSystemUtil { public void setPermissions(@NotNull final String path, final int permissions) throws Exception { myLibC.chmod(path, permissions & LibC.PERM_MASK); } + + @NotNull + @Override + public String getName() { + return "JnaUnix"; + } } + private static class StandardMediatorImpl implements Mediator { // from java.io.FileSystem private static final int BA_REGULAR = 0x02; @@ -452,5 +475,11 @@ public class FileSystemUtil { @Override public void setPermissions(@NotNull final String path, final int permissions) throws Exception { } + + @NotNull + @Override + public String getName() { + return "fallback"; + } } } diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesNio2ReadingTest.java b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesNio2ReadingTest.java index ffb2674334b0..68f2e88ae01e 100644 --- a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesNio2ReadingTest.java +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesNio2ReadingTest.java @@ -20,6 +20,7 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; public class FileAttributesNio2ReadingTest extends FileAttributesReadingTest { @@ -27,6 +28,7 @@ public class FileAttributesNio2ReadingTest extends FileAttributesReadingTest { public static void setUpClass() throws Exception { System.setProperty(FileSystemUtil.FORCE_USE_NIO2_KEY, "true"); FileSystemUtil.resetMediator(); + assertEquals("NIO2", FileSystemUtil.getMediatorName()); } @AfterClass diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java index 56bdc6fc4147..b713b2d38fab 100644 --- a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java @@ -19,6 +19,7 @@ import com.intellij.openapi.util.SystemInfo; import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import java.io.File; @@ -30,6 +31,12 @@ public class FileAttributesReadingTest { private final byte[] myTestData = new byte[]{'t', 'e', 's', 't'}; private File myTempDirectory; + @BeforeClass + public static void checkMediator() throws Exception { + final String expectedName = SystemInfo.isWindows ? "IdeaWin32" : "JnaUnix"; + assertEquals(expectedName, FileSystemUtil.getMediatorName()); + } + @Before public void setUp() throws Exception { myTempDirectory = FileUtil.createTempDirectory(getClass().getName(), ".tmp"); @@ -116,7 +123,7 @@ public class FileAttributesReadingTest { @Test public void linkToDirectory() throws Exception { final File file = FileUtil.createTempDirectory(myTempDirectory, "test.", ".tmp"); - assertTrue(file.setWritable(false, false)); + if (SystemInfo.isUnix) assertTrue(file.setWritable(false, false)); assertTrue(file.setLastModified(file.lastModified() - 5000)); final File link = IoTestUtil.createTempLink(file.getPath(), new File(myTempDirectory, "link").getPath()); @@ -124,7 +131,7 @@ public class FileAttributesReadingTest { assertEquals(FileAttributes.DIRECTORY | FileAttributes.SYM_LINK, attributes.type); assertEquals(file.length(), attributes.length); assertTimestampEquals(file.lastModified(), attributes.lastModified); - assertFalse(attributes.isWritable()); + if (SystemInfo.isUnix) assertFalse(attributes.isWritable()); final String target = FileSystemUtil.resolveSymLink(link); assertEquals(file.getPath(), target); From 2fa869f812dd92163632b3bbb982db0e482c709a Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 23 Jul 2012 14:43:33 +0400 Subject: [PATCH 31/40] IDEA-27620 XML Catalog support: initial --- ...ghtPlatformCodeInsightFixtureTestCase.java | 24 ++++-- .../javaee/ExternalResourceConfigurable.java | 12 ++- .../javaee/ExternalResourceManagerEx.java | 3 + .../javaee/ExternalResourceManagerImpl.java | 37 +++++++- .../javaee/XMLCatalogConfigurable.form | 32 +++++++ .../javaee/XMLCatalogConfigurable.java | 69 +++++++++++++++ .../intellij/javaee/XMLCatalogManager.java | 85 +++++++++++++++++++ .../intellij/xml/XMLCatalogManagerTest.java | 69 +++++++++++++++ xml/tests/testData/catalog/catalog.properties | 4 + xml/tests/testData/catalog/catalog.xml | 8 ++ 10 files changed, 331 insertions(+), 12 deletions(-) create mode 100644 xml/impl/src/com/intellij/javaee/XMLCatalogConfigurable.form create mode 100644 xml/impl/src/com/intellij/javaee/XMLCatalogConfigurable.java create mode 100644 xml/impl/src/com/intellij/javaee/XMLCatalogManager.java create mode 100644 xml/tests/src/com/intellij/xml/XMLCatalogManagerTest.java create mode 100644 xml/tests/testData/catalog/catalog.properties create mode 100644 xml/tests/testData/catalog/catalog.xml diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/LightPlatformCodeInsightFixtureTestCase.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/LightPlatformCodeInsightFixtureTestCase.java index 851b9e11a965..07d31747eadb 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/LightPlatformCodeInsightFixtureTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/LightPlatformCodeInsightFixtureTestCase.java @@ -26,6 +26,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.PsiManager; import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.PlatformTestUtil; import com.intellij.testFramework.UsefulTestCase; import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl; import org.jetbrains.annotations.NonNls; @@ -77,15 +78,20 @@ public abstract class LightPlatformCodeInsightFixtureTestCase extends UsefulTest return null; } - /** - * Return absolute path to the test data. - * - * @return absolute path to the test data. - */ - @NonNls - protected String getTestDataPath() { - return PathManager.getHomePath().replace(File.separatorChar, '/') + getBasePath(); - } + /** + * Return absolute path to the test data. Not intended to be overridden. + * + * @return absolute path to the test data. + */ + @NonNls + protected String getTestDataPath() { + String path = isCommunity() ? PlatformTestUtil.getCommunityPath() : PathManager.getHomePath(); + return path.replace(File.separatorChar, '/') + getBasePath(); + } + + protected boolean isCommunity() { + return false; + } @Override protected void runTest() throws Throwable { diff --git a/xml/impl/src/com/intellij/javaee/ExternalResourceConfigurable.java b/xml/impl/src/com/intellij/javaee/ExternalResourceConfigurable.java index c0009cd8fd13..8c1e5d7e2022 100644 --- a/xml/impl/src/com/intellij/javaee/ExternalResourceConfigurable.java +++ b/xml/impl/src/com/intellij/javaee/ExternalResourceConfigurable.java @@ -44,7 +44,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class ExternalResourceConfigurable extends BaseConfigurable implements SearchableConfigurable, OptionalConfigurable, Configurable.NoScroll { +public class ExternalResourceConfigurable extends BaseConfigurable + implements SearchableConfigurable, OptionalConfigurable, Configurable.NoScroll, Configurable.Composite { private JPanel myPanel; private List myPairs; private List myIgnoredUrls; @@ -54,6 +55,7 @@ public class ExternalResourceConfigurable extends BaseConfigurable implements Se private HtmlLanguageLevelForm myHtmlLanguageLevelForm; @Nullable private final Project myProject; private final List myNewPairs; + private Configurable[] myConfigurables; public ExternalResourceConfigurable(@Nullable Project project) { this(project, Collections.emptyList()); @@ -282,6 +284,14 @@ public class ExternalResourceConfigurable extends BaseConfigurable implements Se return dialog.getPair().myName; } + @Override + public Configurable[] getConfigurables() { + if (myConfigurables == null) { + myConfigurables = new Configurable[]{new XMLCatalogConfigurable()}; + } + return myConfigurables; + } + private static class PathRenderer extends DefaultTableCellRenderer { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final Component rendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); diff --git a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerEx.java b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerEx.java index f22e181b0b05..d327ac4beb7c 100644 --- a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerEx.java +++ b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerEx.java @@ -59,5 +59,8 @@ public abstract class ExternalResourceManagerEx extends ExternalResourceManager public abstract void setDefaultHtmlDoctype(@NotNull String defaultHtmlDoctype, @NotNull Project project); + public abstract String getCatalogPropertiesFile(); + public abstract void setCatalogPropertiesFile(String filePath); + public abstract long getModificationCount(@NotNull Project project); } diff --git a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java index 8e102fef1123..0441f83b15aa 100644 --- a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java +++ b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java @@ -35,6 +35,7 @@ import com.intellij.util.containers.HashMap; import com.intellij.xml.Html5SchemaProvider; import com.intellij.xml.XmlSchemaProvider; import com.intellij.xml.util.XmlUtil; +import com.sun.org.apache.xml.internal.resolver.CatalogManager; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -57,6 +58,7 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple @NonNls public static final String J2EE_1_2 = "http://java.sun.com/j2ee/dtds/"; @NonNls public static final String J2EE_NS = "http://java.sun.com/xml/ns/j2ee/"; @NonNls public static final String JAVAEE_NS = "http://java.sun.com/xml/ns/javaee/"; + private static final String CATALOG_PROPERTIES_ELEMENT = "CATALOG_PROPERTIES"; private final Map> myResources = new HashMap>(); @@ -75,6 +77,10 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple private String myDefaultHtmlDoctype = Html5SchemaProvider.HTML5_SCHEMA_LOCATION; + private String myCatalogPropertiesFile; + private CatalogManager myCatalogManager; + + protected Map> computeStdResources() { ResourceRegistrarImpl registrar = new ResourceRegistrarImpl(); for (StandardResourceProvider provider : Extensions.getExtensions(StandardResourceProvider.EP_NAME)) { @@ -140,6 +146,7 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple if (result == null) { result = getStdResource(url, version); } +// getCatalogManager().getCatalog().resolveURI() if (result == null) { result = url; } @@ -349,9 +356,13 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple } Element child = element.getChild(HTML_DEFAULT_DOCTYPE_ELEMENT); - if (child != null && child.getText() != null) { + if (child != null) { myDefaultHtmlDoctype = child.getText(); } + Element catalogElement = element.getChild(CATALOG_PROPERTIES_ELEMENT); + if (catalogElement != null) { + myCatalogPropertiesFile = catalogElement.getTextTrim(); + } } public void writeExternal(Element element) throws WriteExternalException { @@ -380,7 +391,11 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple e.setText(myDefaultHtmlDoctype); element.addContent(e); } - + if (myCatalogPropertiesFile != null) { + Element properties = new Element(CATALOG_PROPERTIES_ELEMENT); + properties.setText(myCatalogPropertiesFile); + element.addContent(properties); + } final ReplacePathToMacroMap macroReplacements = new ReplacePathToMacroMap(); PathMacrosImpl.getInstanceEx().addMacroReplacements(macroReplacements); macroReplacements.substitute(element, SystemInfo.isFileSystemCaseSensitive); @@ -423,6 +438,24 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple getProjectResources(project).setDefaultHtmlDoctype(defaultHtmlDoctype); } + @Override + public String getCatalogPropertiesFile() { + return myCatalogPropertiesFile; + } + + @Override + public void setCatalogPropertiesFile(String filePath) { + myCatalogManager = null; + myCatalogPropertiesFile = filePath; + } + + private CatalogManager getCatalogManager() { + if (myCatalogManager == null) { + myCatalogManager = new CatalogManager(myCatalogPropertiesFile); + } + return myCatalogManager; + } + private void setDefaultHtmlDoctype(String defaultHtmlDoctype) { myModificationCount++; myDefaultHtmlDoctype = defaultHtmlDoctype; diff --git a/xml/impl/src/com/intellij/javaee/XMLCatalogConfigurable.form b/xml/impl/src/com/intellij/javaee/XMLCatalogConfigurable.form new file mode 100644 index 000000000000..660dca5dbcf7 --- /dev/null +++ b/xml/impl/src/com/intellij/javaee/XMLCatalogConfigurable.form @@ -0,0 +1,32 @@ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/impl/src/com/intellij/javaee/XMLCatalogConfigurable.java b/xml/impl/src/com/intellij/javaee/XMLCatalogConfigurable.java new file mode 100644 index 000000000000..5f39dbd840f4 --- /dev/null +++ b/xml/impl/src/com/intellij/javaee/XMLCatalogConfigurable.java @@ -0,0 +1,69 @@ +/* + * 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.javaee; + +import com.intellij.openapi.options.BaseConfigurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import org.jetbrains.annotations.Nls; + +import javax.swing.*; + +/** + * @author Dmitry Avdeev + * Date: 7/20/12 + */ +public class XMLCatalogConfigurable extends BaseConfigurable { + + private TextFieldWithBrowseButton myPropertyFile; + private JPanel myPanel; + + @Nls + @Override + public String getDisplayName() { + return "XML Catalog"; + } + + @Override + public String getHelpTopic() { + return null; + } + + @Override + public JComponent createComponent() { + return myPanel; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myPropertyFile.getTextField(); + } + + @Override + public void apply() throws ConfigurationException { + ExternalResourceManagerEx.getInstanceEx().setCatalogPropertiesFile(myPropertyFile.getText()); + } + + @Override + public void reset() { + myPropertyFile.setText(ExternalResourceManagerEx.getInstanceEx().getCatalogPropertiesFile()); + } + + @Override + public void disposeUIResources() { + + } +} diff --git a/xml/impl/src/com/intellij/javaee/XMLCatalogManager.java b/xml/impl/src/com/intellij/javaee/XMLCatalogManager.java new file mode 100644 index 000000000000..180126bcd7c8 --- /dev/null +++ b/xml/impl/src/com/intellij/javaee/XMLCatalogManager.java @@ -0,0 +1,85 @@ +/* + * 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.javaee; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.io.FileUtil; +import com.sun.org.apache.xml.internal.resolver.CatalogManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.util.PropertyResourceBundle; + +/** + * @author Dmitry Avdeev + * Date: 7/20/12 + */ +public class XMLCatalogManager { + + private final static Logger LOG = Logger.getInstance(XMLCatalogManager.class); + + private static Field ourResources; + private static Field ourPropertyFileUri; + + static { + try { + ourResources = CatalogManager.class.getDeclaredField("resources"); + ourResources.setAccessible(true); + ourPropertyFileUri = CatalogManager.class.getDeclaredField("propertyFileURI"); + ourPropertyFileUri.setAccessible(true); + } + catch (NoSuchFieldException e) { + LOG.error(e); + } + } + + private final CatalogManager myManager = new CatalogManager(); + + public XMLCatalogManager(@NotNull String propertiesFilePath) throws IOException { + + File file = new File(propertiesFilePath); + String s = FileUtil.loadFile(file); + PropertyResourceBundle bundle = new PropertyResourceBundle(new StringReader(s)); + try { + ourResources.set(myManager, bundle); + ourPropertyFileUri.set(myManager, file.toURI().toURL()); + } + catch (IllegalAccessException e) { + LOG.error(e); + } + } + + @Nullable + public String resolve(String uri) { + try { + return myManager.getCatalog().resolvePublic(uri, null); + } + catch (IOException e) { + LOG.warn(e); + return null; + } + } + + @TestOnly + public CatalogManager getManager() { + return myManager; + } +} diff --git a/xml/tests/src/com/intellij/xml/XMLCatalogManagerTest.java b/xml/tests/src/com/intellij/xml/XMLCatalogManagerTest.java new file mode 100644 index 000000000000..39987f0119e5 --- /dev/null +++ b/xml/tests/src/com/intellij/xml/XMLCatalogManagerTest.java @@ -0,0 +1,69 @@ +/* + * 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.xml; + +import com.intellij.javaee.XMLCatalogManager; +import com.intellij.testFramework.IdeaTestCase; +import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; +import com.sun.org.apache.xml.internal.resolver.CatalogManager; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.util.Vector; + +/** + * @author Dmitry Avdeev + * Date: 7/20/12 + */ +@SuppressWarnings("UseOfObsoleteCollectionType") +public class XMLCatalogManagerTest extends LightPlatformCodeInsightFixtureTestCase { + + public void testCatalogManager() throws Exception { + XMLCatalogManager manager = getManager(); + CatalogManager catalogManager = manager.getManager(); + Vector files = catalogManager.getCatalogFiles(); + assertEquals(1, files.size()); + String filePath = (String)files.get(0); + assertTrue(filePath, filePath.endsWith("catalog.xml")); + assertTrue(filePath, new File(new URI(filePath)).exists()); + } + + public void testResolve() throws Exception { + String resolve = getManager().resolve("-//W3C//DTD XHTML 1.0 Strict//EN"); + assertNotNull(resolve); + assertTrue(resolve, resolve.endsWith("/catalog/xhtml1-strict.dtd")); + } + + private XMLCatalogManager getManager() throws IOException { + return new XMLCatalogManager(getTestDataPath() + "catalog.properties"); + } + + @Override + protected String getBasePath() { + return "/xml/tests/testData/catalog/"; + } + + @Override + protected boolean isCommunity() { + return true; + } + + @SuppressWarnings("JUnitTestCaseWithNonTrivialConstructors") + public XMLCatalogManagerTest() { + IdeaTestCase.initPlatformPrefix(); + } +} diff --git a/xml/tests/testData/catalog/catalog.properties b/xml/tests/testData/catalog/catalog.properties new file mode 100644 index 000000000000..a6cb7b857b3d --- /dev/null +++ b/xml/tests/testData/catalog/catalog.properties @@ -0,0 +1,4 @@ +# Catalogs are relative to this properties file +relative-catalogs=false +# Catalog list +catalogs=catalog.xml \ No newline at end of file diff --git a/xml/tests/testData/catalog/catalog.xml b/xml/tests/testData/catalog/catalog.xml new file mode 100644 index 000000000000..acb48871b955 --- /dev/null +++ b/xml/tests/testData/catalog/catalog.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file From bcaa348e2d1130ce8a44ed18f3856adb889f94b2 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 23 Jul 2012 15:35:34 +0400 Subject: [PATCH 32/40] IDEA-27620 XML Catalog support: initial --- .../intellij/javaee/ExternalResourceManagerImpl.java | 10 +++++----- .../src/com/intellij/javaee/XMLCatalogManager.java | 11 +++++++---- .../src/com/intellij/xml/XMLCatalogManagerTest.java | 2 +- xml/tests/xml-tests.iml | 1 + 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java index 0441f83b15aa..e3fe4ba5cf8f 100644 --- a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java +++ b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java @@ -35,7 +35,6 @@ import com.intellij.util.containers.HashMap; import com.intellij.xml.Html5SchemaProvider; import com.intellij.xml.XmlSchemaProvider; import com.intellij.xml.util.XmlUtil; -import com.sun.org.apache.xml.internal.resolver.CatalogManager; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -78,7 +77,7 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple private String myDefaultHtmlDoctype = Html5SchemaProvider.HTML5_SCHEMA_LOCATION; private String myCatalogPropertiesFile; - private CatalogManager myCatalogManager; + private XMLCatalogManager myCatalogManager; protected Map> computeStdResources() { @@ -449,9 +448,10 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple myCatalogPropertiesFile = filePath; } - private CatalogManager getCatalogManager() { - if (myCatalogManager == null) { - myCatalogManager = new CatalogManager(myCatalogPropertiesFile); + @Nullable + private XMLCatalogManager getCatalogManager() { + if (myCatalogManager == null && myCatalogPropertiesFile != null) { + myCatalogManager = new XMLCatalogManager(myCatalogPropertiesFile); } return myCatalogManager; } diff --git a/xml/impl/src/com/intellij/javaee/XMLCatalogManager.java b/xml/impl/src/com/intellij/javaee/XMLCatalogManager.java index 180126bcd7c8..fbc0e5037017 100644 --- a/xml/impl/src/com/intellij/javaee/XMLCatalogManager.java +++ b/xml/impl/src/com/intellij/javaee/XMLCatalogManager.java @@ -17,7 +17,7 @@ package com.intellij.javaee; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; -import com.sun.org.apache.xml.internal.resolver.CatalogManager; +import org.apache.xml.resolver.CatalogManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; @@ -53,18 +53,21 @@ public class XMLCatalogManager { private final CatalogManager myManager = new CatalogManager(); - public XMLCatalogManager(@NotNull String propertiesFilePath) throws IOException { + public XMLCatalogManager(@NotNull String propertiesFilePath) { File file = new File(propertiesFilePath); - String s = FileUtil.loadFile(file); - PropertyResourceBundle bundle = new PropertyResourceBundle(new StringReader(s)); try { + String s = FileUtil.loadFile(file); + PropertyResourceBundle bundle = new PropertyResourceBundle(new StringReader(s)); ourResources.set(myManager, bundle); ourPropertyFileUri.set(myManager, file.toURI().toURL()); } catch (IllegalAccessException e) { LOG.error(e); } + catch (IOException e) { + LOG.warn(e); + } } @Nullable diff --git a/xml/tests/src/com/intellij/xml/XMLCatalogManagerTest.java b/xml/tests/src/com/intellij/xml/XMLCatalogManagerTest.java index 39987f0119e5..4309b2e7110d 100644 --- a/xml/tests/src/com/intellij/xml/XMLCatalogManagerTest.java +++ b/xml/tests/src/com/intellij/xml/XMLCatalogManagerTest.java @@ -18,7 +18,7 @@ package com.intellij.xml; import com.intellij.javaee.XMLCatalogManager; import com.intellij.testFramework.IdeaTestCase; import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; -import com.sun.org.apache.xml.internal.resolver.CatalogManager; +import org.apache.xml.resolver.CatalogManager; import java.io.File; import java.io.IOException; diff --git a/xml/tests/xml-tests.iml b/xml/tests/xml-tests.iml index 10dee0f83aee..a6274e349d36 100644 --- a/xml/tests/xml-tests.iml +++ b/xml/tests/xml-tests.iml @@ -17,6 +17,7 @@ + From d54477c34b1643fb68539bf6c235dc001582ec9c Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 23 Jul 2012 12:07:34 +0200 Subject: [PATCH 33/40] Maven: test fixed by removing unnecessary and obsolete assertion --- .../jetbrains/idea/maven/compiler/ResourceFilteringTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java index 5c1fdbead33a..7117306ba443 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java @@ -15,7 +15,6 @@ */ package org.jetbrains.idea.maven.compiler; -import com.intellij.compiler.CompilerConfiguration; import com.intellij.openapi.application.Result; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.fileTypes.FileTypeManager; @@ -911,7 +910,6 @@ public class ResourceFilteringTest extends MavenImportingTestCase { } public void testDoNotFilterButCopyBigFiles() throws Exception { - assertFalse(CompilerConfiguration.getInstance(myProject).isResourceFile("file.xyz")); assertEquals(FileTypeManager.getInstance().getFileTypeByFileName("file.xyz"), FileTypes.UNKNOWN); createProjectSubFile("resources/file.xyz").setBinaryContent(new byte[1024 * 1024 * 20]); From db772ebfe53aa455e2d7cc194ad2bb2e4fb9471d Mon Sep 17 00:00:00 2001 From: "andrey.zaytsev" Date: Fri, 20 Jul 2012 14:51:22 +0400 Subject: [PATCH 34/40] master breakpoint chooser blinking --- .../intellij/debugger/ui/breakpoints/BreakpointChooser.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointChooser.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointChooser.java index e22fd8030aa0..6027682ba85b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointChooser.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointChooser.java @@ -33,6 +33,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import java.util.Collection; public class BreakpointChooser { @@ -149,7 +150,7 @@ public class BreakpointChooser { }; setSelectedBreakpoint(baseBreakpoint); myActionToolbar = ActionManager.getInstance().createActionToolbar("asdad", new DefaultActionGroup(myComboBoxAction), true); - + myActionToolbar.setLayoutPolicy(ActionToolbar.WRAP_LAYOUT_POLICY); } public void setBreakpointItems(Collection items) { From cb4153d23526474061cb232696f5adc255304a2c Mon Sep 17 00:00:00 2001 From: "andrey.zaytsev" Date: Tue, 17 Jul 2012 20:10:28 +0400 Subject: [PATCH 35/40] testNotSuspendingBreakpoint --- .../intellij/openapi/util/text/StringUtil.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index 896c93774a37..62e9ef562fea 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -1953,6 +1953,22 @@ public class StringUtil extends StringUtilRt { return res; } + public static int getOccurrenceCount(@NotNull String text, final String s) { + int res = 0; + int i = 0; + while (i < text.length()) { + i = text.indexOf(s, i); + if (i >= 0) { + res++; + i++; + } + else { + break; + } + } + return res; + } + @NotNull public static String fixVariableNameDerivedFromPropertyName(@NotNull String name) { char c = name.charAt(0); From 43c27ebaef551a6c4b68bde2a3d8da6015981dda Mon Sep 17 00:00:00 2001 From: "andrey.zaytsev" Date: Mon, 23 Jul 2012 16:49:44 +0400 Subject: [PATCH 36/40] IDEA-87924 Debugger: Ease defining breakpoint condition. Condition checkbox enables when condition is empty --- .../debugger/ui/breakpoints/BreakpointPropertiesPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java index e7323b367208..2465d872de87 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.java @@ -570,7 +570,7 @@ public abstract class BreakpointPropertiesPanel { PsiElement context = breakpoint.getEvaluationElement(); myPassCountCheckbox.setSelected(breakpoint.COUNT_FILTER_ENABLED); - myConditionCheckbox.setSelected(breakpoint.CONDITION_ENABLED); + myConditionCheckbox.setSelected(breakpoint.CONDITION_ENABLED || breakpoint.getCondition() == null || breakpoint.getCondition().isEmpty()); myConditionCombo.setEnabled(myBreakpoint.CONDITION_ENABLED); From cf2879d77c2592c95b82c951f86eecd9af07760c Mon Sep 17 00:00:00 2001 From: Andrey Vokin Date: Fri, 20 Jul 2012 17:03:07 +0400 Subject: [PATCH 37/40] Cucumber-jvm. Navigation from step to step definition in java implemented --- .../testFramework/EditorTestUtil.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java index 087af416f1f3..48e4ef5b045f 100644 --- a/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java @@ -25,6 +25,7 @@ import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.psi.tree.IElementType; import junit.framework.Assert; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -35,6 +36,10 @@ import java.util.List; * Time: 21:00:49 */ public class EditorTestUtil { + public static final String CARET_TAG = ""; + public static final String SELECTION_START_TAG = ""; + public static final String SELECTION_END_TAG = ""; + public static final char BACKSPACE_FAKE_CHAR = '\uFFFF'; public static final char SMART_ENTER_FAKE_CHAR = '\uFFFE'; @@ -83,4 +88,33 @@ public class EditorTestUtil { } return tokens; } + + public static int getCaretPosition(@NotNull final String content) { + return getCaretAndSelectionPosition(content)[0]; + } + + public static int[] getCaretAndSelectionPosition(@NotNull final String content) { + int caretPosInSourceFile = content.indexOf(CARET_TAG); + int selectionStartInSourceFile = content.indexOf(SELECTION_START_TAG); + int selectionEndInSourceFile = content.indexOf(SELECTION_END_TAG); + if (selectionStartInSourceFile >= 0) { + if (caretPosInSourceFile >= 0) { + if (caretPosInSourceFile < selectionStartInSourceFile) { + selectionStartInSourceFile -= CARET_TAG.length(); + selectionEndInSourceFile -= CARET_TAG.length(); + } + else { + if (caretPosInSourceFile < selectionEndInSourceFile) { + caretPosInSourceFile -= SELECTION_START_TAG.length(); + } + else { + caretPosInSourceFile -= SELECTION_START_TAG.length() + SELECTION_END_TAG.length(); + } + } + } + selectionEndInSourceFile -= SELECTION_START_TAG.length(); + } + + return new int[]{caretPosInSourceFile, selectionStartInSourceFile, selectionEndInSourceFile}; + } } From 834a07e691276217c268d996e7e45a1fc1e0e674 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 23 Jul 2012 13:32:29 +0200 Subject: [PATCH 38/40] IDEA-89157 Open File should remember the last location (If i open a file, it should initially point to the project's root dir) --- .../src/com/intellij/ide/actions/OpenFileAction.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/actions/OpenFileAction.java b/platform/platform-impl/src/com/intellij/ide/actions/OpenFileAction.java index 4d979d195d5b..12c04a57c9bf 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/OpenFileAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/OpenFileAction.java @@ -25,6 +25,7 @@ import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileElement; import com.intellij.openapi.fileChooser.PathChooserDialog; +import com.intellij.openapi.fileChooser.impl.FileChooserUtil; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager; @@ -96,7 +97,7 @@ public class OpenFileAction extends AnAction implements DumbAware { @NotNull final List result) { for (final VirtualFile file : result) { if (file.isDirectory()) { - ProjectUtil.openOrImport(file.getPath(), project, false); + FileChooserUtil.setLastOpenedFile(ProjectUtil.openOrImport(file.getPath(), project, false), file); return; } @@ -106,7 +107,7 @@ public class OpenFileAction extends AnAction implements DumbAware { IdeBundle.message("title.open.project"), Messages.getQuestionIcon()); if (answer == 0) { - ProjectUtil.openOrImport(file.getPath(), project, false); + FileChooserUtil.setLastOpenedFile(ProjectUtil.openOrImport(file.getPath(), project, false), file); return; } } From 112fa67418b77112efe74a72a3226db58e92fbea Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 23 Jul 2012 15:09:47 +0200 Subject: [PATCH 39/40] PsiUtil.isAnnotationMethod --- .../JavaClassNameInsertHandler.java | 2 +- .../completion/JavaCompletionContributor.java | 17 +- .../completion/PreferByKindWeigher.java | 3 +- .../analysis/AnnotationsHighlightUtil.java | 6 +- .../impl/analysis/GenericsHighlightUtil.java | 2 +- .../daemon/impl/analysis/HighlightUtil.java | 2 +- .../impl/analysis/HighlightVisitorImpl.java | 2 +- .../generation/OverrideImplementUtil.java | 1616 ++++++++--------- .../impls/AnnotationParameterInfoHandler.java | 3 +- .../psi/impl/search/MethodUsagesSearcher.java | 3 +- ...PsiAnnotationMethodReferencesSearcher.java | 3 +- .../src/com/intellij/psi/util/PsiUtil.java | 6 + .../com/intellij/psi/impl/PsiImplUtil.java | 2 +- .../util/InitializerRequirement.java | 3 +- 14 files changed, 845 insertions(+), 825 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java index 0a56c5aa0f38..db375547f0c9 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java @@ -154,7 +154,7 @@ class JavaClassNameInsertHandler implements InsertHandler 0) { + if (PsiUtil.isAnnotationMethod(list.getParent()) && list.getParametersCount() > 0) { final String message = JavaErrorMessages.message("annotation.interface.members.may.not.have.parameters"); return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, list, message); } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 64d6da201569..88be76a50040 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -190,7 +190,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh if (parent instanceof PsiNameValuePair) { method = (PsiMethod)parent.getReference().resolve(); } - else if (parent instanceof PsiAnnotationMethod) { + else if (PsiUtil.isAnnotationMethod(parent)) { method = (PsiMethod)parent; } if (method != null) { diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java b/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java index c45adeebd78a..b8a48486348c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java @@ -1,808 +1,808 @@ -/* - * Copyright 2000-2011 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.codeInsight.generation; - -import com.intellij.codeInsight.AnnotationUtil; -import com.intellij.codeInsight.CodeInsightActionHandler; -import com.intellij.codeInsight.CodeInsightBundle; -import com.intellij.codeInsight.MethodImplementor; -import com.intellij.codeInsight.intention.AddAnnotationFix; -import com.intellij.featureStatistics.FeatureUsageTracker; -import com.intellij.featureStatistics.ProductivityFeatureNames; -import com.intellij.icons.AllIcons; -import com.intellij.ide.fileTemplates.FileTemplate; -import com.intellij.ide.fileTemplates.FileTemplateManager; -import com.intellij.ide.fileTemplates.FileTemplateUtil; -import com.intellij.ide.fileTemplates.JavaTemplateUtil; -import com.intellij.ide.util.MemberChooser; -import com.intellij.ide.util.PropertiesComponent; -import com.intellij.lang.java.JavaLanguage; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.Result; -import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.ScrollType; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.FileTypeManager; -import com.intellij.openapi.keymap.Keymap; -import com.intellij.openapi.keymap.KeymapManager; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleUtil; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.psi.impl.source.jsp.jspJava.JspClass; -import com.intellij.psi.infos.CandidateInfo; -import com.intellij.psi.javadoc.PsiDocComment; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.*; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.util.*; - -public class OverrideImplementUtil { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.OverrideImplementUtil"); - - @NonNls private static final String PROP_COMBINED_OVERRIDE_IMPLEMENT = "OverrideImplement.combined"; - - private OverrideImplementUtil() { - } - - @NotNull - public static Collection getMethodsToOverrideImplement(PsiClass aClass, boolean toImplement) { - return getMapToOverrideImplement(aClass, toImplement).values(); - } - - @NotNull - public static Collection getMethodSignaturesToImplement(@NotNull PsiClass aClass) { - return getMapToOverrideImplement(aClass, true).keySet(); - } - - @NotNull - public static Collection getMethodSignaturesToOverride(@NotNull PsiClass aClass) { - return getMapToOverrideImplement(aClass, false).keySet(); - } - - @NotNull - private static Map getMapToOverrideImplement(PsiClass aClass, boolean toImplement) { - Map abstracts = new LinkedHashMap(); - Map finals = new LinkedHashMap(); - Map concretes = new LinkedHashMap(); - - LOG.assertTrue(aClass.isValid()); - Collection allMethodSigs = aClass.getVisibleSignatures(); - PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper(); - for (HierarchicalMethodSignature signature : allMethodSigs) { - PsiMethod method = signature.getMethod(); - LOG.assertTrue(method.isValid(), aClass); - - if (method.hasModifierProperty(PsiModifier.STATIC) || !resolveHelper.isAccessible(method, aClass, aClass)) continue; - PsiClass hisClass = method.getContainingClass(); - if (hisClass == null) continue; - // filter non-immediate super constructors - if (method.isConstructor() && (!aClass.isInheritor(hisClass, false) || aClass instanceof PsiAnonymousClass || aClass.isEnum())) { - continue; - } - // filter already implemented - if (MethodSignatureUtil.findMethodBySignature(aClass, signature, false) != null) { - continue; - } - - if (method.hasModifierProperty(PsiModifier.FINAL)) { - finals.put(signature, method); - continue; - } - - Map map = hisClass.isInterface() || method.hasModifierProperty(PsiModifier.ABSTRACT) ? abstracts : concretes; - PsiMethod other = map.get(signature); - if (other == null || preferLeftForImplement(method, other)) { - map.put(signature, method); - } - } - - final Map result = new TreeMap(new MethodSignatureComparator()); - if (toImplement || aClass.isInterface()) { - collectMethodsToImplement(aClass, abstracts, finals, concretes, result); - } - else { - for (Map.Entry entry : concretes.entrySet()) { - MethodSignature signature = entry.getKey(); - PsiMethod concrete = entry.getValue(); - if (finals.get(signature) == null) { - PsiMethod abstractOne = abstracts.get(signature); - if (abstractOne == null || !abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true) || - CommonClassNames.JAVA_LANG_OBJECT.equals(concrete.getContainingClass().getQualifiedName())) { - PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(concrete, signature.getSubstitutor()); - CandidateInfo info = new CandidateInfo(concrete, subst); - result.put(signature, info); - } - } - } - } - - return result; - } - - public static void collectMethodsToImplement(PsiClass aClass, - Map abstracts, - Map finals, - Map concretes, - Map result) { - for (Map.Entry entry : abstracts.entrySet()) { - MethodSignature signature = entry.getKey(); - PsiMethod abstractOne = entry.getValue(); - PsiMethod concrete = concretes.get(signature); - if (concrete == null - || PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList()) - || !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)) { - if (finals.get(signature) == null) { - PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(abstractOne, signature.getSubstitutor()); - CandidateInfo info = new CandidateInfo(abstractOne, subst); - result.put(signature, info); - } - } - } - - for (final MethodImplementor implementor : getImplementors()) { - for (final PsiMethod method : implementor.getMethodsToImplement(aClass)) { - MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(), - method.getTypeParameterList(), PsiSubstitutor.EMPTY, method.isConstructor()); - CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY); - result.put(signature, info); - } - } - } - - private static boolean preferLeftForImplement(PsiMethod left, PsiMethod right) { - if (PsiUtil.getAccessLevel(left.getModifierList()) > PsiUtil.getAccessLevel(right.getModifierList())) return true; - if (!left.getContainingClass().isInterface()) return true; - if (!right.getContainingClass().isInterface()) return false; - // implement annotated method - PsiAnnotation[] leftAnnotations = left.getModifierList().getAnnotations(); - PsiAnnotation[] rightAnnotations = right.getModifierList().getAnnotations(); - return leftAnnotations.length > rightAnnotations.length; - } - - private static MethodImplementor[] getImplementors() { - return Extensions.getExtensions(MethodImplementor.EXTENSION_POINT_NAME); - } - - /** - * generate methods (with bodies) corresponding to given method declaration - * there are maybe two method implementations for one declaration - * (e.g. EJB' create() -> ejbCreate(), ejbPostCreate() ) - * @param aClass context for method implementations - * @param method method to override or implement - * @param toCopyJavaDoc true if copy JavaDoc from method declaration - * @return list of method prototypes - */ - @NotNull - public static Collection overrideOrImplementMethod(PsiClass aClass, PsiMethod method, boolean toCopyJavaDoc) throws IncorrectOperationException { - final PsiClass containingClass = method.getContainingClass(); - LOG.assertTrue(containingClass != null); - PsiSubstitutor substitutor = aClass.isInheritor(containingClass, true) - ? TypeConversionUtil.getSuperClassSubstitutor(containingClass, aClass, PsiSubstitutor.EMPTY) - : PsiSubstitutor.EMPTY; - return overrideOrImplementMethod(aClass, method, substitutor, toCopyJavaDoc, CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION); - } - - public static boolean isInsertOverride(PsiMethod superMethod, PsiClass targetClass) { - if (!CodeStyleSettingsManager.getSettings(targetClass.getProject()).INSERT_OVERRIDE_ANNOTATION) { - return false; - } - return canInsertOverride(superMethod, targetClass); - } - - public static boolean canInsertOverride(PsiMethod superMethod, PsiClass targetClass) { - if (superMethod.isConstructor() || superMethod.hasModifierProperty(PsiModifier.STATIC)) { - return false; - } - if (!PsiUtil.isLanguageLevel5OrHigher(targetClass)) { - return false; - } - if (PsiUtil.isLanguageLevel6OrHigher(targetClass)) return true; - if (targetClass.isInterface()) return true; - PsiClass superClass = superMethod.getContainingClass(); - return !superClass.isInterface(); - } - - @NotNull - private static Collection overrideOrImplementMethod(PsiClass aClass, - PsiMethod method, - PsiSubstitutor substitutor, - boolean toCopyJavaDoc, - boolean insertOverrideIfPossible) throws IncorrectOperationException { - if (!method.isValid() || !substitutor.isValid()) return Collections.emptyList(); - - List results = new ArrayList(); - for (final MethodImplementor implementor : getImplementors()) { - final PsiMethod[] prototypes = implementor.createImplementationPrototypes(aClass, method); - if (implementor.isBodyGenerated()) { - ContainerUtil.addAll(results, prototypes); - } - else { - for (PsiMethod prototype : prototypes) { - results.add(decorateMethod(aClass, method, toCopyJavaDoc, insertOverrideIfPossible, prototype)); - } - } - } - if (results.isEmpty()) { - PsiMethod method1 = GenerateMembersUtil.substituteGenericMethod(method, substitutor, aClass); - - PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory(); - PsiMethod result = (PsiMethod)factory.createClass("Dummy").add(method1); - if (result instanceof PsiAnnotationMethod) { - PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)result).getDefaultValue(); - if (defaultValue != null) { - PsiElement defaultKeyword = defaultValue; - while (!(defaultKeyword instanceof PsiKeyword) && defaultKeyword != null) { - defaultKeyword = defaultKeyword.getPrevSibling(); - } - if (defaultKeyword == null) defaultKeyword = defaultValue; - defaultValue.getParent().deleteChildRange(defaultKeyword, defaultValue); - } - } - results.add(decorateMethod(aClass, method, toCopyJavaDoc, insertOverrideIfPossible, result)); - } - - for (Iterator iterator = results.iterator(); iterator.hasNext();) { - if (aClass.findMethodBySignature(iterator.next(), false) != null) { - iterator.remove(); - } - } - - return results; - } - - private static PsiMethod decorateMethod(PsiClass aClass, - PsiMethod method, - boolean toCopyJavaDoc, - boolean insertOverrideIfPossible, - PsiMethod result) { - PsiUtil.setModifierProperty(result, PsiModifier.ABSTRACT, aClass.isInterface()); - PsiUtil.setModifierProperty(result, PsiModifier.NATIVE, false); - - if (!toCopyJavaDoc){ - PsiDocComment comment = result.getDocComment(); - if (comment != null){ - comment.delete(); - } - } - - //method type params are not allowed when overriding from raw type - final PsiTypeParameterList list = result.getTypeParameterList(); - if (list != null) { - final PsiClass containingClass = method.getContainingClass(); - if (containingClass != null) { - for (PsiClassType classType : aClass.getSuperTypes()) { - if (InheritanceUtil.isInheritorOrSelf(PsiUtil.resolveClassInType(classType), containingClass, true) && classType.isRaw()) { - list.replace(JavaPsiFacade.getElementFactory(aClass.getProject()).createTypeParameterList()); - break; - } - } - } - } - - annotateOnOverrideImplement(result, aClass, method, insertOverrideIfPossible); - - if (CodeStyleSettingsManager.getSettings(aClass.getProject()).REPEAT_SYNCHRONIZED && method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) { - result.getModifierList().setModifierProperty(PsiModifier.SYNCHRONIZED, true); - } - - final PsiCodeBlock body = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createCodeBlockFromText("{}", null); - PsiCodeBlock oldbody = result.getBody(); - if (oldbody != null){ - oldbody.replace(body); - } - else{ - result.add(body); - } - - setupMethodBody(result, method, aClass); - - // probably, it's better to reformat the whole method - it can go from other style sources - final Project project = method.getProject(); - CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); - CommonCodeStyleSettings javaSettings = CodeStyleSettingsManager.getSettings(project).getCommonSettings(JavaLanguage.INSTANCE); - boolean keepBreaks = javaSettings.KEEP_LINE_BREAKS; - javaSettings.KEEP_LINE_BREAKS = false; - result = (PsiMethod)JavaCodeStyleManager.getInstance(project).shortenClassReferences(result); - result = (PsiMethod)codeStyleManager.reformat(result); - javaSettings.KEEP_LINE_BREAKS = keepBreaks; - return result; - } - - public static void annotateOnOverrideImplement(PsiMethod method, PsiClass targetClass, PsiMethod overridden) { - annotateOnOverrideImplement(method, targetClass, overridden, - CodeStyleSettingsManager.getSettings(method.getProject()).INSERT_OVERRIDE_ANNOTATION); - } - - public static void annotateOnOverrideImplement(PsiMethod method, PsiClass targetClass, PsiMethod overridden, boolean insertOverride) { - if (insertOverride && canInsertOverride(overridden, targetClass)) { - annotate(method, Override.class.getName()); - } - final Module module = ModuleUtil.findModuleForPsiElement(targetClass); - final GlobalSearchScope moduleScope = module != null ? GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module) : null; - final Project project = targetClass.getProject(); - final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); - for (OverrideImplementsAnnotationsHandler each : Extensions.getExtensions(OverrideImplementsAnnotationsHandler.EP_NAME)) { - for (String annotation : each.getAnnotations(project)) { - if (moduleScope != null && facade.findClass(annotation, moduleScope) == null) continue; - if (AnnotationUtil.isAnnotated(overridden, annotation, false)) { - annotate(method, annotation, each.annotationsToRemove(project, annotation)); - } - } - } - } - - public static void annotate(@NotNull PsiMethod result, String fqn, String... annosToRemove) throws IncorrectOperationException { - Project project = result.getProject(); - AddAnnotationFix fix = new AddAnnotationFix(fqn, result, annosToRemove); - if (fix.isAvailable(project, null, result.getContainingFile())) { - fix.invoke(project, null, result.getContainingFile()); - } - } - - public static boolean isOverridable(PsiMethod method) { - return !method.isConstructor() - && !method.hasModifierProperty(PsiModifier.STATIC) - && !method.hasModifierProperty(PsiModifier.FINAL) - && !method.hasModifierProperty(PsiModifier.PRIVATE); - } - - @NotNull - public static List> overrideOrImplementMethods(PsiClass aClass, - Collection candidates, - boolean toCopyJavaDoc, - boolean toInsertAtOverride) - throws IncorrectOperationException { - List candidateInfos = ContainerUtil.map2List(candidates, new Function() { - public CandidateInfo fun(final PsiMethodMember s) { - return new CandidateInfo(s.getElement(), s.getSubstitutor()); - } - }); - final List methods = overrideOrImplementMethodCandidates(aClass, candidateInfos, toCopyJavaDoc, toInsertAtOverride); - return convert2GenerationInfos(methods); - } - - @NotNull - public static List overrideOrImplementMethodCandidates(PsiClass aClass, - Collection candidates, - boolean toCopyJavaDoc, - boolean insertOverrideWherePossible) throws IncorrectOperationException { - List result = new ArrayList(); - for (CandidateInfo candidateInfo : candidates) { - result.addAll(overrideOrImplementMethod(aClass, (PsiMethod)candidateInfo.getElement(), candidateInfo.getSubstitutor(), - toCopyJavaDoc, insertOverrideWherePossible)); - } - return result; - } - - public static List> convert2GenerationInfos(final Collection methods) { - return ContainerUtil.map2List(methods, new Function>() { - public PsiGenerationInfo fun(final PsiMethod s) { - return createGenerationInfo(s); - } - }); - } - - public static PsiGenerationInfo createGenerationInfo(PsiMethod s) { - return createGenerationInfo(s, true); - } - - public static PsiGenerationInfo createGenerationInfo(PsiMethod s, boolean mergeIfExists) { - for (MethodImplementor implementor : getImplementors()) { - final GenerationInfo info = implementor.createGenerationInfo(s, mergeIfExists); - if (info instanceof PsiGenerationInfo) return (PsiGenerationInfo)info; - } - return new PsiGenerationInfo(s); - } - - @NotNull - public static String callSuper (PsiMethod superMethod, PsiMethod overriding) { - @NonNls StringBuilder buffer = new StringBuilder(); - if (!superMethod.isConstructor() && superMethod.getReturnType() != PsiType.VOID) { - buffer.append("return "); - } - buffer.append("super"); - PsiParameter[] parms = overriding.getParameterList().getParameters(); - if (!superMethod.isConstructor()){ - buffer.append("."); - buffer.append(superMethod.getName()); - } - buffer.append("("); - for (int i = 0; i < parms.length; i++) { - String name = parms[i].getName(); - if (i > 0) buffer.append(","); - buffer.append(name); - } - buffer.append(")"); - return buffer.toString(); - } - - public static void setupMethodBody(PsiMethod result, PsiMethod originalMethod, PsiClass targetClass) throws IncorrectOperationException { - String templName = originalMethod.hasModifierProperty(PsiModifier.ABSTRACT) ? - JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY; - FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templName); - setupMethodBody(result, originalMethod, targetClass, template); - } - - public static void setupMethodBody(final PsiMethod result, final PsiMethod originalMethod, final PsiClass targetClass, - final FileTemplate template) throws IncorrectOperationException { - if (targetClass.isInterface()) { - final PsiCodeBlock body = result.getBody(); - if (body != null) body.delete(); - } - - FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(template.getExtension()); - PsiType returnType = result.getReturnType(); - if (returnType == null) { - returnType = PsiType.VOID; - } - Properties properties = new Properties(); - properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, returnType.getPresentableText()); - properties.setProperty(FileTemplate.ATTRIBUTE_DEFAULT_RETURN_VALUE, PsiTypesUtil.getDefaultValueOfType(returnType)); - properties.setProperty(FileTemplate.ATTRIBUTE_CALL_SUPER, callSuper(originalMethod, result)); - JavaTemplateUtil.setClassAndMethodNameProperties(properties, targetClass, result); - - JVMElementFactory factory = JVMElementFactories.getFactory(targetClass.getLanguage(), originalMethod.getProject()); - if (factory == null) factory = JavaPsiFacade.getInstance(originalMethod.getProject()).getElementFactory(); - @NonNls String methodText; - try { - String bodyText = template.getText(properties); - if (bodyText != null && !bodyText.isEmpty()) bodyText += "\n"; - methodText = "void foo () {\n" + bodyText + "}"; - methodText = FileTemplateUtil.indent(methodText, result.getProject(), fileType); - } catch (Exception e) { - throw new IncorrectOperationException("Failed to parse file template",e); - } - if (methodText != null) { - PsiMethod m; - try { - m = factory.createMethodFromText(methodText, originalMethod); - } - catch (IncorrectOperationException e) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - Messages.showErrorDialog(CodeInsightBundle.message("override.implement.broken.file.template.message"), - CodeInsightBundle.message("override.implement.broken.file.template.title")); - } - }); - return; - } - PsiCodeBlock oldBody = result.getBody(); - if (oldBody != null) { - oldBody.replace(m.getBody()); - } - } - } - - public static void chooseAndOverrideMethods(Project project, Editor editor, PsiClass aClass){ - FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT); - chooseAndOverrideOrImplementMethods(project, editor, aClass, false); - } - - public static void chooseAndImplementMethods(Project project, Editor editor, PsiClass aClass){ - FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT); - chooseAndOverrideOrImplementMethods(project, editor, aClass, true); - } - - public static void chooseAndOverrideOrImplementMethods(final Project project, - final Editor editor, - final PsiClass aClass, - final boolean toImplement){ - LOG.assertTrue(aClass.isValid()); - ApplicationManager.getApplication().assertReadAccessAllowed(); - - Collection candidates = getMethodsToOverrideImplement(aClass, toImplement); - Collection secondary = toImplement || aClass.isInterface() ? Collections.emptyList() : getMethodsToOverrideImplement(aClass, true); - - final MemberChooser chooser = showOverrideImplementChooser(editor, aClass, toImplement, candidates, secondary); - if (chooser == null) return; - - final List selectedElements = chooser.getSelectedElements(); - if (selectedElements == null || selectedElements.isEmpty()) return; - - LOG.assertTrue(aClass.isValid()); - new WriteCommandAction(project, aClass.getContainingFile()) { - protected void run(final Result result) throws Throwable { - overrideOrImplementMethodsInRightPlace(editor, aClass, selectedElements, chooser.isCopyJavadoc(), chooser.isInsertOverrideAnnotation()); - } - }.execute(); - } - - @Nullable - public static MemberChooser showOverrideImplementChooser(Editor editor, - final PsiElement aClass, - final boolean toImplement, - Collection candidates, - Collection secondary) { - Project project = aClass.getProject(); - if (candidates.isEmpty() && secondary.isEmpty()) return null; - - final PsiMethodMember[] onlyPrimary = convertToMethodMembers(candidates); - final PsiMethodMember[] all = ArrayUtil.mergeArrays(onlyPrimary, convertToMethodMembers(secondary)); - - final String toMerge = PropertiesComponent.getInstance(project).getValue(PROP_COMBINED_OVERRIDE_IMPLEMENT); - final Ref merge = Ref.create(!"false".equals(toMerge)); - - final boolean isAll = merge.get().booleanValue(); - final MemberChooser chooser = new MemberChooser(isAll ? all : onlyPrimary, false, true, project, - PsiUtil.isLanguageLevel5OrHigher(aClass)) { - - @Override - protected void fillToolbarActions(DefaultActionGroup group) { - super.fillToolbarActions(group); - if (toImplement) return; - - final ToggleAction mergeAction = new ToggleAction("Show methods to implement", "Show methods to implement", - AllIcons.General.Show_to_implement) { - @Override - public boolean isSelected(AnActionEvent e) { - return merge.get().booleanValue(); - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - merge.set(state); - resetElements(state ? all : onlyPrimary); - setTitle(getChooserTitle(false, merge)); - } - }; - mergeAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.ALT_MASK)), myTree); - - Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts("OverrideMethods"); - mergeAction.registerCustomShortcutSet(new CustomShortcutSet(shortcuts), myTree); - - group.add(mergeAction); - } - }; - chooser.setTitle(getChooserTitle(toImplement, merge)); - registerHandlerForComplementaryAction(project, editor, aClass, toImplement, chooser); - - chooser.setCopyJavadocVisible(true); - - if (toImplement) { - chooser.selectElements(isAll ? all : onlyPrimary); - } - - if (ApplicationManager.getApplication().isUnitTestMode()) { - chooser.selectElements(all); - chooser.close(DialogWrapper.OK_EXIT_CODE); - return chooser; - } - - chooser.show(); - if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null; - - PropertiesComponent.getInstance(project).setValue(PROP_COMBINED_OVERRIDE_IMPLEMENT, merge.get().toString()); - return chooser; - } - - private static String getChooserTitle(boolean toImplement, Ref merge) { - return toImplement - ? CodeInsightBundle.message("methods.to.implement.chooser.title") - : merge.get().booleanValue() - ? CodeInsightBundle.message("methods.to.override.implement.chooser.title") - : CodeInsightBundle.message("methods.to.override.chooser.title"); - } - - private static PsiMethodMember[] convertToMethodMembers(Collection candidates) { - return ContainerUtil.map2Array(candidates, PsiMethodMember.class, new Function() { - public PsiMethodMember fun(final CandidateInfo s) { - return new PsiMethodMember(s); - } - }); - } - - private static void registerHandlerForComplementaryAction(final Project project, final Editor editor, final PsiElement aClass, - final boolean toImplement, - final MemberChooser chooser) { - final JComponent preferredFocusedComponent = chooser.getPreferredFocusedComponent(); - final Keymap keymap = KeymapManager.getInstance().getActiveKeymap(); - - @NonNls final String s = toImplement ? "OverrideMethods" : "ImplementMethods"; - final Shortcut[] shortcuts = keymap.getShortcuts(s); - - if (shortcuts.length > 0 && shortcuts[0] instanceof KeyboardShortcut) { - preferredFocusedComponent.getInputMap().put( - ((KeyboardShortcut)shortcuts[0]).getFirstKeyStroke(), s - ); - - preferredFocusedComponent.getActionMap().put( - s, - new AbstractAction() { - public void actionPerformed(final ActionEvent e) { - chooser.close(DialogWrapper.CANCEL_EXIT_CODE); - - // invoke later in order to close previous modal dialog - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - final CodeInsightActionHandler handler = toImplement ? new OverrideMethodsHandler(): new ImplementMethodsHandler(); - handler.invoke(project, editor, aClass.getContainingFile()); - } - }); - } - } - ); - } - } - - public static void overrideOrImplementMethodsInRightPlace(Editor editor, - PsiClass aClass, - Collection candidates, - boolean copyJavadoc, - boolean insertOverrideWherePossible) { - try { - int offset = editor.getCaretModel().getOffset(); - if (aClass.getLBrace() == null) { - PsiClass psiClass = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createClass("X"); - aClass.addRangeAfter(psiClass.getLBrace(), psiClass.getRBrace(), aClass.getLastChild()); - } - - int lbraceOffset = aClass.getLBrace().getTextOffset(); - List> resultMembers; - if (offset <= lbraceOffset || aClass.isEnum()) { - resultMembers = new ArrayList>(); - for (PsiMethodMember candidate : candidates) { - Collection prototypes = - overrideOrImplementMethod(aClass, candidate.getElement(), candidate.getSubstitutor(), copyJavadoc, insertOverrideWherePossible); - List> infos = convert2GenerationInfos(prototypes); - for (PsiGenerationInfo info : infos) { - PsiElement anchor = getDefaultAnchorToOverrideOrImplement(aClass, candidate.getElement(), candidate.getSubstitutor()); - info.insert(aClass, anchor, true); - resultMembers.add(info); - } - } - } - else { - List> prototypes = overrideOrImplementMethods(aClass, candidates, copyJavadoc, insertOverrideWherePossible); - resultMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes); - } - - if (!resultMembers.isEmpty()) { - resultMembers.get(0).positionCaret(editor, true); - } - } - catch (IncorrectOperationException e) { - LOG.error(e); - } - } - - @Nullable - public static PsiElement getDefaultAnchorToOverrideOrImplement(PsiClass aClass, PsiMethod baseMethod, PsiSubstitutor substitutor){ - PsiMethod prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(baseMethod, PsiMethod.class); - while(prevBaseMethod != null) { - String name = prevBaseMethod.isConstructor() ? aClass.getName() : prevBaseMethod.getName(); - //Happens when aClass instanceof PsiAnonymousClass - if (name != null) { - MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, prevBaseMethod.getParameterList(), prevBaseMethod.getTypeParameterList(), substitutor, prevBaseMethod.isConstructor()); - PsiMethod prevMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false); - if (prevMethod != null){ - return prevMethod.getNextSibling(); - } - } - prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(prevBaseMethod, PsiMethod.class); - } - - PsiMethod nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(baseMethod, PsiMethod.class); - while(nextBaseMethod != null) { - String name = nextBaseMethod.isConstructor() ? aClass.getName() : nextBaseMethod.getName(); - if (name != null) { - MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, nextBaseMethod.getParameterList(), nextBaseMethod.getTypeParameterList(), substitutor, nextBaseMethod.isConstructor()); - PsiMethod nextMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false); - if (nextMethod != null){ - return nextMethod; - } - } - nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(nextBaseMethod, PsiMethod.class); - } - - return null; - } - - public static void overrideOrImplement(PsiClass psiClass, @NotNull PsiMethod baseMethod) throws IncorrectOperationException { - FileEditorManager fileEditorManager = FileEditorManager.getInstance(baseMethod.getProject()); - - List> prototypes = convert2GenerationInfos(overrideOrImplementMethod(psiClass, baseMethod, false)); - if (prototypes.isEmpty()) return; - - PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseMethod.getContainingClass(), psiClass, PsiSubstitutor.EMPTY); - PsiElement anchor = getDefaultAnchorToOverrideOrImplement(psiClass, baseMethod, substitutor); - List> results = GenerateMembersUtil.insertMembersBeforeAnchor(psiClass, anchor, prototypes); - - PsiFile psiFile = psiClass.getContainingFile(); - Editor editor = fileEditorManager.openTextEditor(new OpenFileDescriptor(psiFile.getProject(), psiFile.getVirtualFile()), false); - if (editor == null) return; - - results.get(0).positionCaret(editor, true); - editor.getScrollingModel().scrollToCaret(ScrollType.CENTER); - } - - @Nullable - public static PsiClass getContextClass(Project project, Editor editor, PsiFile file, boolean allowInterface) { - PsiDocumentManager.getInstance(project).commitAllDocuments(); - - int offset = editor.getCaretModel().getOffset(); - PsiElement element = file.findElementAt(offset); - do { - element = PsiTreeUtil.getParentOfType(element, PsiClass.class); - } - while (element instanceof PsiTypeParameter); - - final PsiClass aClass = (PsiClass)element; - if (aClass instanceof JspClass) return null; - return aClass == null || !allowInterface && aClass.isInterface() ? null : aClass; - } - - public static void overrideOrImplementMethodsInRightPlace(Editor editor1, PsiClass aClass, Collection members, boolean copyJavadoc) { - boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION; - overrideOrImplementMethodsInRightPlace(editor1, aClass, members, copyJavadoc, insert); - } - - public static List overrideOrImplementMethodCandidates(PsiClass aClass, Collection candidatesToImplement, - boolean copyJavadoc) throws IncorrectOperationException { - boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION; - return overrideOrImplementMethodCandidates(aClass, candidatesToImplement, copyJavadoc, insert); - } - - public static class MethodSignatureComparator implements Comparator { - // signatures should appear in the order of declaration - public int compare(MethodSignature o1, MethodSignature o2) { - if (o1 instanceof MethodSignatureBackedByPsiMethod && o2 instanceof MethodSignatureBackedByPsiMethod) { - PsiMethod m1 = ((MethodSignatureBackedByPsiMethod)o1).getMethod(); - PsiMethod m2 = ((MethodSignatureBackedByPsiMethod)o2).getMethod(); - PsiClass c1 = m1.getContainingClass(); - PsiClass c2 = m2.getContainingClass(); - if (c1 != null && c2 != null) { - if (c1 == c2) { - final List methods = Arrays.asList(c1.getMethods()); - return methods.indexOf(m1) - methods.indexOf(m2); - } - - if (c1.isInheritor(c2, true)) return -1; - if (c2.isInheritor(c1, true)) return 1; - - return StringUtil.notNullize(c1.getName()).compareTo(StringUtil.notNullize(c2.getName())); - } - return m1.getTextOffset() - m2.getTextOffset(); - } - return 0; - } - } -} +/* + * Copyright 2000-2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.generation; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.CodeInsightActionHandler; +import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.codeInsight.MethodImplementor; +import com.intellij.codeInsight.intention.AddAnnotationFix; +import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.featureStatistics.ProductivityFeatureNames; +import com.intellij.icons.AllIcons; +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.ide.fileTemplates.JavaTemplateUtil; +import com.intellij.ide.util.MemberChooser; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.keymap.Keymap; +import com.intellij.openapi.keymap.KeymapManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.impl.source.jsp.jspJava.JspClass; +import com.intellij.psi.infos.CandidateInfo; +import com.intellij.psi.javadoc.PsiDocComment; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.*; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.*; + +public class OverrideImplementUtil { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.OverrideImplementUtil"); + + @NonNls private static final String PROP_COMBINED_OVERRIDE_IMPLEMENT = "OverrideImplement.combined"; + + private OverrideImplementUtil() { + } + + @NotNull + public static Collection getMethodsToOverrideImplement(PsiClass aClass, boolean toImplement) { + return getMapToOverrideImplement(aClass, toImplement).values(); + } + + @NotNull + public static Collection getMethodSignaturesToImplement(@NotNull PsiClass aClass) { + return getMapToOverrideImplement(aClass, true).keySet(); + } + + @NotNull + public static Collection getMethodSignaturesToOverride(@NotNull PsiClass aClass) { + return getMapToOverrideImplement(aClass, false).keySet(); + } + + @NotNull + private static Map getMapToOverrideImplement(PsiClass aClass, boolean toImplement) { + Map abstracts = new LinkedHashMap(); + Map finals = new LinkedHashMap(); + Map concretes = new LinkedHashMap(); + + LOG.assertTrue(aClass.isValid()); + Collection allMethodSigs = aClass.getVisibleSignatures(); + PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper(); + for (HierarchicalMethodSignature signature : allMethodSigs) { + PsiMethod method = signature.getMethod(); + LOG.assertTrue(method.isValid(), aClass); + + if (method.hasModifierProperty(PsiModifier.STATIC) || !resolveHelper.isAccessible(method, aClass, aClass)) continue; + PsiClass hisClass = method.getContainingClass(); + if (hisClass == null) continue; + // filter non-immediate super constructors + if (method.isConstructor() && (!aClass.isInheritor(hisClass, false) || aClass instanceof PsiAnonymousClass || aClass.isEnum())) { + continue; + } + // filter already implemented + if (MethodSignatureUtil.findMethodBySignature(aClass, signature, false) != null) { + continue; + } + + if (method.hasModifierProperty(PsiModifier.FINAL)) { + finals.put(signature, method); + continue; + } + + Map map = hisClass.isInterface() || method.hasModifierProperty(PsiModifier.ABSTRACT) ? abstracts : concretes; + PsiMethod other = map.get(signature); + if (other == null || preferLeftForImplement(method, other)) { + map.put(signature, method); + } + } + + final Map result = new TreeMap(new MethodSignatureComparator()); + if (toImplement || aClass.isInterface()) { + collectMethodsToImplement(aClass, abstracts, finals, concretes, result); + } + else { + for (Map.Entry entry : concretes.entrySet()) { + MethodSignature signature = entry.getKey(); + PsiMethod concrete = entry.getValue(); + if (finals.get(signature) == null) { + PsiMethod abstractOne = abstracts.get(signature); + if (abstractOne == null || !abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true) || + CommonClassNames.JAVA_LANG_OBJECT.equals(concrete.getContainingClass().getQualifiedName())) { + PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(concrete, signature.getSubstitutor()); + CandidateInfo info = new CandidateInfo(concrete, subst); + result.put(signature, info); + } + } + } + } + + return result; + } + + public static void collectMethodsToImplement(PsiClass aClass, + Map abstracts, + Map finals, + Map concretes, + Map result) { + for (Map.Entry entry : abstracts.entrySet()) { + MethodSignature signature = entry.getKey(); + PsiMethod abstractOne = entry.getValue(); + PsiMethod concrete = concretes.get(signature); + if (concrete == null + || PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList()) + || !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)) { + if (finals.get(signature) == null) { + PsiSubstitutor subst = GenerateMembersUtil.correctSubstitutor(abstractOne, signature.getSubstitutor()); + CandidateInfo info = new CandidateInfo(abstractOne, subst); + result.put(signature, info); + } + } + } + + for (final MethodImplementor implementor : getImplementors()) { + for (final PsiMethod method : implementor.getMethodsToImplement(aClass)) { + MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(), + method.getTypeParameterList(), PsiSubstitutor.EMPTY, method.isConstructor()); + CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY); + result.put(signature, info); + } + } + } + + private static boolean preferLeftForImplement(PsiMethod left, PsiMethod right) { + if (PsiUtil.getAccessLevel(left.getModifierList()) > PsiUtil.getAccessLevel(right.getModifierList())) return true; + if (!left.getContainingClass().isInterface()) return true; + if (!right.getContainingClass().isInterface()) return false; + // implement annotated method + PsiAnnotation[] leftAnnotations = left.getModifierList().getAnnotations(); + PsiAnnotation[] rightAnnotations = right.getModifierList().getAnnotations(); + return leftAnnotations.length > rightAnnotations.length; + } + + private static MethodImplementor[] getImplementors() { + return Extensions.getExtensions(MethodImplementor.EXTENSION_POINT_NAME); + } + + /** + * generate methods (with bodies) corresponding to given method declaration + * there are maybe two method implementations for one declaration + * (e.g. EJB' create() -> ejbCreate(), ejbPostCreate() ) + * @param aClass context for method implementations + * @param method method to override or implement + * @param toCopyJavaDoc true if copy JavaDoc from method declaration + * @return list of method prototypes + */ + @NotNull + public static Collection overrideOrImplementMethod(PsiClass aClass, PsiMethod method, boolean toCopyJavaDoc) throws IncorrectOperationException { + final PsiClass containingClass = method.getContainingClass(); + LOG.assertTrue(containingClass != null); + PsiSubstitutor substitutor = aClass.isInheritor(containingClass, true) + ? TypeConversionUtil.getSuperClassSubstitutor(containingClass, aClass, PsiSubstitutor.EMPTY) + : PsiSubstitutor.EMPTY; + return overrideOrImplementMethod(aClass, method, substitutor, toCopyJavaDoc, CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION); + } + + public static boolean isInsertOverride(PsiMethod superMethod, PsiClass targetClass) { + if (!CodeStyleSettingsManager.getSettings(targetClass.getProject()).INSERT_OVERRIDE_ANNOTATION) { + return false; + } + return canInsertOverride(superMethod, targetClass); + } + + public static boolean canInsertOverride(PsiMethod superMethod, PsiClass targetClass) { + if (superMethod.isConstructor() || superMethod.hasModifierProperty(PsiModifier.STATIC)) { + return false; + } + if (!PsiUtil.isLanguageLevel5OrHigher(targetClass)) { + return false; + } + if (PsiUtil.isLanguageLevel6OrHigher(targetClass)) return true; + if (targetClass.isInterface()) return true; + PsiClass superClass = superMethod.getContainingClass(); + return !superClass.isInterface(); + } + + @NotNull + private static Collection overrideOrImplementMethod(PsiClass aClass, + PsiMethod method, + PsiSubstitutor substitutor, + boolean toCopyJavaDoc, + boolean insertOverrideIfPossible) throws IncorrectOperationException { + if (!method.isValid() || !substitutor.isValid()) return Collections.emptyList(); + + List results = new ArrayList(); + for (final MethodImplementor implementor : getImplementors()) { + final PsiMethod[] prototypes = implementor.createImplementationPrototypes(aClass, method); + if (implementor.isBodyGenerated()) { + ContainerUtil.addAll(results, prototypes); + } + else { + for (PsiMethod prototype : prototypes) { + results.add(decorateMethod(aClass, method, toCopyJavaDoc, insertOverrideIfPossible, prototype)); + } + } + } + if (results.isEmpty()) { + PsiMethod method1 = GenerateMembersUtil.substituteGenericMethod(method, substitutor, aClass); + + PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory(); + PsiMethod result = (PsiMethod)factory.createClass("Dummy").add(method1); + if (PsiUtil.isAnnotationMethod(result)) { + PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)result).getDefaultValue(); + if (defaultValue != null) { + PsiElement defaultKeyword = defaultValue; + while (!(defaultKeyword instanceof PsiKeyword) && defaultKeyword != null) { + defaultKeyword = defaultKeyword.getPrevSibling(); + } + if (defaultKeyword == null) defaultKeyword = defaultValue; + defaultValue.getParent().deleteChildRange(defaultKeyword, defaultValue); + } + } + results.add(decorateMethod(aClass, method, toCopyJavaDoc, insertOverrideIfPossible, result)); + } + + for (Iterator iterator = results.iterator(); iterator.hasNext();) { + if (aClass.findMethodBySignature(iterator.next(), false) != null) { + iterator.remove(); + } + } + + return results; + } + + private static PsiMethod decorateMethod(PsiClass aClass, + PsiMethod method, + boolean toCopyJavaDoc, + boolean insertOverrideIfPossible, + PsiMethod result) { + PsiUtil.setModifierProperty(result, PsiModifier.ABSTRACT, aClass.isInterface()); + PsiUtil.setModifierProperty(result, PsiModifier.NATIVE, false); + + if (!toCopyJavaDoc){ + PsiDocComment comment = result.getDocComment(); + if (comment != null){ + comment.delete(); + } + } + + //method type params are not allowed when overriding from raw type + final PsiTypeParameterList list = result.getTypeParameterList(); + if (list != null) { + final PsiClass containingClass = method.getContainingClass(); + if (containingClass != null) { + for (PsiClassType classType : aClass.getSuperTypes()) { + if (InheritanceUtil.isInheritorOrSelf(PsiUtil.resolveClassInType(classType), containingClass, true) && classType.isRaw()) { + list.replace(JavaPsiFacade.getElementFactory(aClass.getProject()).createTypeParameterList()); + break; + } + } + } + } + + annotateOnOverrideImplement(result, aClass, method, insertOverrideIfPossible); + + if (CodeStyleSettingsManager.getSettings(aClass.getProject()).REPEAT_SYNCHRONIZED && method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) { + result.getModifierList().setModifierProperty(PsiModifier.SYNCHRONIZED, true); + } + + final PsiCodeBlock body = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createCodeBlockFromText("{}", null); + PsiCodeBlock oldbody = result.getBody(); + if (oldbody != null){ + oldbody.replace(body); + } + else{ + result.add(body); + } + + setupMethodBody(result, method, aClass); + + // probably, it's better to reformat the whole method - it can go from other style sources + final Project project = method.getProject(); + CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); + CommonCodeStyleSettings javaSettings = CodeStyleSettingsManager.getSettings(project).getCommonSettings(JavaLanguage.INSTANCE); + boolean keepBreaks = javaSettings.KEEP_LINE_BREAKS; + javaSettings.KEEP_LINE_BREAKS = false; + result = (PsiMethod)JavaCodeStyleManager.getInstance(project).shortenClassReferences(result); + result = (PsiMethod)codeStyleManager.reformat(result); + javaSettings.KEEP_LINE_BREAKS = keepBreaks; + return result; + } + + public static void annotateOnOverrideImplement(PsiMethod method, PsiClass targetClass, PsiMethod overridden) { + annotateOnOverrideImplement(method, targetClass, overridden, + CodeStyleSettingsManager.getSettings(method.getProject()).INSERT_OVERRIDE_ANNOTATION); + } + + public static void annotateOnOverrideImplement(PsiMethod method, PsiClass targetClass, PsiMethod overridden, boolean insertOverride) { + if (insertOverride && canInsertOverride(overridden, targetClass)) { + annotate(method, Override.class.getName()); + } + final Module module = ModuleUtil.findModuleForPsiElement(targetClass); + final GlobalSearchScope moduleScope = module != null ? GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module) : null; + final Project project = targetClass.getProject(); + final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + for (OverrideImplementsAnnotationsHandler each : Extensions.getExtensions(OverrideImplementsAnnotationsHandler.EP_NAME)) { + for (String annotation : each.getAnnotations(project)) { + if (moduleScope != null && facade.findClass(annotation, moduleScope) == null) continue; + if (AnnotationUtil.isAnnotated(overridden, annotation, false)) { + annotate(method, annotation, each.annotationsToRemove(project, annotation)); + } + } + } + } + + public static void annotate(@NotNull PsiMethod result, String fqn, String... annosToRemove) throws IncorrectOperationException { + Project project = result.getProject(); + AddAnnotationFix fix = new AddAnnotationFix(fqn, result, annosToRemove); + if (fix.isAvailable(project, null, result.getContainingFile())) { + fix.invoke(project, null, result.getContainingFile()); + } + } + + public static boolean isOverridable(PsiMethod method) { + return !method.isConstructor() + && !method.hasModifierProperty(PsiModifier.STATIC) + && !method.hasModifierProperty(PsiModifier.FINAL) + && !method.hasModifierProperty(PsiModifier.PRIVATE); + } + + @NotNull + public static List> overrideOrImplementMethods(PsiClass aClass, + Collection candidates, + boolean toCopyJavaDoc, + boolean toInsertAtOverride) + throws IncorrectOperationException { + List candidateInfos = ContainerUtil.map2List(candidates, new Function() { + public CandidateInfo fun(final PsiMethodMember s) { + return new CandidateInfo(s.getElement(), s.getSubstitutor()); + } + }); + final List methods = overrideOrImplementMethodCandidates(aClass, candidateInfos, toCopyJavaDoc, toInsertAtOverride); + return convert2GenerationInfos(methods); + } + + @NotNull + public static List overrideOrImplementMethodCandidates(PsiClass aClass, + Collection candidates, + boolean toCopyJavaDoc, + boolean insertOverrideWherePossible) throws IncorrectOperationException { + List result = new ArrayList(); + for (CandidateInfo candidateInfo : candidates) { + result.addAll(overrideOrImplementMethod(aClass, (PsiMethod)candidateInfo.getElement(), candidateInfo.getSubstitutor(), + toCopyJavaDoc, insertOverrideWherePossible)); + } + return result; + } + + public static List> convert2GenerationInfos(final Collection methods) { + return ContainerUtil.map2List(methods, new Function>() { + public PsiGenerationInfo fun(final PsiMethod s) { + return createGenerationInfo(s); + } + }); + } + + public static PsiGenerationInfo createGenerationInfo(PsiMethod s) { + return createGenerationInfo(s, true); + } + + public static PsiGenerationInfo createGenerationInfo(PsiMethod s, boolean mergeIfExists) { + for (MethodImplementor implementor : getImplementors()) { + final GenerationInfo info = implementor.createGenerationInfo(s, mergeIfExists); + if (info instanceof PsiGenerationInfo) return (PsiGenerationInfo)info; + } + return new PsiGenerationInfo(s); + } + + @NotNull + public static String callSuper (PsiMethod superMethod, PsiMethod overriding) { + @NonNls StringBuilder buffer = new StringBuilder(); + if (!superMethod.isConstructor() && superMethod.getReturnType() != PsiType.VOID) { + buffer.append("return "); + } + buffer.append("super"); + PsiParameter[] parms = overriding.getParameterList().getParameters(); + if (!superMethod.isConstructor()){ + buffer.append("."); + buffer.append(superMethod.getName()); + } + buffer.append("("); + for (int i = 0; i < parms.length; i++) { + String name = parms[i].getName(); + if (i > 0) buffer.append(","); + buffer.append(name); + } + buffer.append(")"); + return buffer.toString(); + } + + public static void setupMethodBody(PsiMethod result, PsiMethod originalMethod, PsiClass targetClass) throws IncorrectOperationException { + String templName = originalMethod.hasModifierProperty(PsiModifier.ABSTRACT) ? + JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY; + FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templName); + setupMethodBody(result, originalMethod, targetClass, template); + } + + public static void setupMethodBody(final PsiMethod result, final PsiMethod originalMethod, final PsiClass targetClass, + final FileTemplate template) throws IncorrectOperationException { + if (targetClass.isInterface()) { + final PsiCodeBlock body = result.getBody(); + if (body != null) body.delete(); + } + + FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(template.getExtension()); + PsiType returnType = result.getReturnType(); + if (returnType == null) { + returnType = PsiType.VOID; + } + Properties properties = new Properties(); + properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, returnType.getPresentableText()); + properties.setProperty(FileTemplate.ATTRIBUTE_DEFAULT_RETURN_VALUE, PsiTypesUtil.getDefaultValueOfType(returnType)); + properties.setProperty(FileTemplate.ATTRIBUTE_CALL_SUPER, callSuper(originalMethod, result)); + JavaTemplateUtil.setClassAndMethodNameProperties(properties, targetClass, result); + + JVMElementFactory factory = JVMElementFactories.getFactory(targetClass.getLanguage(), originalMethod.getProject()); + if (factory == null) factory = JavaPsiFacade.getInstance(originalMethod.getProject()).getElementFactory(); + @NonNls String methodText; + try { + String bodyText = template.getText(properties); + if (bodyText != null && !bodyText.isEmpty()) bodyText += "\n"; + methodText = "void foo () {\n" + bodyText + "}"; + methodText = FileTemplateUtil.indent(methodText, result.getProject(), fileType); + } catch (Exception e) { + throw new IncorrectOperationException("Failed to parse file template",e); + } + if (methodText != null) { + PsiMethod m; + try { + m = factory.createMethodFromText(methodText, originalMethod); + } + catch (IncorrectOperationException e) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + Messages.showErrorDialog(CodeInsightBundle.message("override.implement.broken.file.template.message"), + CodeInsightBundle.message("override.implement.broken.file.template.title")); + } + }); + return; + } + PsiCodeBlock oldBody = result.getBody(); + if (oldBody != null) { + oldBody.replace(m.getBody()); + } + } + } + + public static void chooseAndOverrideMethods(Project project, Editor editor, PsiClass aClass){ + FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT); + chooseAndOverrideOrImplementMethods(project, editor, aClass, false); + } + + public static void chooseAndImplementMethods(Project project, Editor editor, PsiClass aClass){ + FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT); + chooseAndOverrideOrImplementMethods(project, editor, aClass, true); + } + + public static void chooseAndOverrideOrImplementMethods(final Project project, + final Editor editor, + final PsiClass aClass, + final boolean toImplement){ + LOG.assertTrue(aClass.isValid()); + ApplicationManager.getApplication().assertReadAccessAllowed(); + + Collection candidates = getMethodsToOverrideImplement(aClass, toImplement); + Collection secondary = toImplement || aClass.isInterface() ? Collections.emptyList() : getMethodsToOverrideImplement(aClass, true); + + final MemberChooser chooser = showOverrideImplementChooser(editor, aClass, toImplement, candidates, secondary); + if (chooser == null) return; + + final List selectedElements = chooser.getSelectedElements(); + if (selectedElements == null || selectedElements.isEmpty()) return; + + LOG.assertTrue(aClass.isValid()); + new WriteCommandAction(project, aClass.getContainingFile()) { + protected void run(final Result result) throws Throwable { + overrideOrImplementMethodsInRightPlace(editor, aClass, selectedElements, chooser.isCopyJavadoc(), chooser.isInsertOverrideAnnotation()); + } + }.execute(); + } + + @Nullable + public static MemberChooser showOverrideImplementChooser(Editor editor, + final PsiElement aClass, + final boolean toImplement, + Collection candidates, + Collection secondary) { + Project project = aClass.getProject(); + if (candidates.isEmpty() && secondary.isEmpty()) return null; + + final PsiMethodMember[] onlyPrimary = convertToMethodMembers(candidates); + final PsiMethodMember[] all = ArrayUtil.mergeArrays(onlyPrimary, convertToMethodMembers(secondary)); + + final String toMerge = PropertiesComponent.getInstance(project).getValue(PROP_COMBINED_OVERRIDE_IMPLEMENT); + final Ref merge = Ref.create(!"false".equals(toMerge)); + + final boolean isAll = merge.get().booleanValue(); + final MemberChooser chooser = new MemberChooser(isAll ? all : onlyPrimary, false, true, project, + PsiUtil.isLanguageLevel5OrHigher(aClass)) { + + @Override + protected void fillToolbarActions(DefaultActionGroup group) { + super.fillToolbarActions(group); + if (toImplement) return; + + final ToggleAction mergeAction = new ToggleAction("Show methods to implement", "Show methods to implement", + AllIcons.General.Show_to_implement) { + @Override + public boolean isSelected(AnActionEvent e) { + return merge.get().booleanValue(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + merge.set(state); + resetElements(state ? all : onlyPrimary); + setTitle(getChooserTitle(false, merge)); + } + }; + mergeAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.ALT_MASK)), myTree); + + Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts("OverrideMethods"); + mergeAction.registerCustomShortcutSet(new CustomShortcutSet(shortcuts), myTree); + + group.add(mergeAction); + } + }; + chooser.setTitle(getChooserTitle(toImplement, merge)); + registerHandlerForComplementaryAction(project, editor, aClass, toImplement, chooser); + + chooser.setCopyJavadocVisible(true); + + if (toImplement) { + chooser.selectElements(isAll ? all : onlyPrimary); + } + + if (ApplicationManager.getApplication().isUnitTestMode()) { + chooser.selectElements(all); + chooser.close(DialogWrapper.OK_EXIT_CODE); + return chooser; + } + + chooser.show(); + if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null; + + PropertiesComponent.getInstance(project).setValue(PROP_COMBINED_OVERRIDE_IMPLEMENT, merge.get().toString()); + return chooser; + } + + private static String getChooserTitle(boolean toImplement, Ref merge) { + return toImplement + ? CodeInsightBundle.message("methods.to.implement.chooser.title") + : merge.get().booleanValue() + ? CodeInsightBundle.message("methods.to.override.implement.chooser.title") + : CodeInsightBundle.message("methods.to.override.chooser.title"); + } + + private static PsiMethodMember[] convertToMethodMembers(Collection candidates) { + return ContainerUtil.map2Array(candidates, PsiMethodMember.class, new Function() { + public PsiMethodMember fun(final CandidateInfo s) { + return new PsiMethodMember(s); + } + }); + } + + private static void registerHandlerForComplementaryAction(final Project project, final Editor editor, final PsiElement aClass, + final boolean toImplement, + final MemberChooser chooser) { + final JComponent preferredFocusedComponent = chooser.getPreferredFocusedComponent(); + final Keymap keymap = KeymapManager.getInstance().getActiveKeymap(); + + @NonNls final String s = toImplement ? "OverrideMethods" : "ImplementMethods"; + final Shortcut[] shortcuts = keymap.getShortcuts(s); + + if (shortcuts.length > 0 && shortcuts[0] instanceof KeyboardShortcut) { + preferredFocusedComponent.getInputMap().put( + ((KeyboardShortcut)shortcuts[0]).getFirstKeyStroke(), s + ); + + preferredFocusedComponent.getActionMap().put( + s, + new AbstractAction() { + public void actionPerformed(final ActionEvent e) { + chooser.close(DialogWrapper.CANCEL_EXIT_CODE); + + // invoke later in order to close previous modal dialog + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + final CodeInsightActionHandler handler = toImplement ? new OverrideMethodsHandler(): new ImplementMethodsHandler(); + handler.invoke(project, editor, aClass.getContainingFile()); + } + }); + } + } + ); + } + } + + public static void overrideOrImplementMethodsInRightPlace(Editor editor, + PsiClass aClass, + Collection candidates, + boolean copyJavadoc, + boolean insertOverrideWherePossible) { + try { + int offset = editor.getCaretModel().getOffset(); + if (aClass.getLBrace() == null) { + PsiClass psiClass = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createClass("X"); + aClass.addRangeAfter(psiClass.getLBrace(), psiClass.getRBrace(), aClass.getLastChild()); + } + + int lbraceOffset = aClass.getLBrace().getTextOffset(); + List> resultMembers; + if (offset <= lbraceOffset || aClass.isEnum()) { + resultMembers = new ArrayList>(); + for (PsiMethodMember candidate : candidates) { + Collection prototypes = + overrideOrImplementMethod(aClass, candidate.getElement(), candidate.getSubstitutor(), copyJavadoc, insertOverrideWherePossible); + List> infos = convert2GenerationInfos(prototypes); + for (PsiGenerationInfo info : infos) { + PsiElement anchor = getDefaultAnchorToOverrideOrImplement(aClass, candidate.getElement(), candidate.getSubstitutor()); + info.insert(aClass, anchor, true); + resultMembers.add(info); + } + } + } + else { + List> prototypes = overrideOrImplementMethods(aClass, candidates, copyJavadoc, insertOverrideWherePossible); + resultMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes); + } + + if (!resultMembers.isEmpty()) { + resultMembers.get(0).positionCaret(editor, true); + } + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + + @Nullable + public static PsiElement getDefaultAnchorToOverrideOrImplement(PsiClass aClass, PsiMethod baseMethod, PsiSubstitutor substitutor){ + PsiMethod prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(baseMethod, PsiMethod.class); + while(prevBaseMethod != null) { + String name = prevBaseMethod.isConstructor() ? aClass.getName() : prevBaseMethod.getName(); + //Happens when aClass instanceof PsiAnonymousClass + if (name != null) { + MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, prevBaseMethod.getParameterList(), prevBaseMethod.getTypeParameterList(), substitutor, prevBaseMethod.isConstructor()); + PsiMethod prevMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false); + if (prevMethod != null){ + return prevMethod.getNextSibling(); + } + } + prevBaseMethod = PsiTreeUtil.getPrevSiblingOfType(prevBaseMethod, PsiMethod.class); + } + + PsiMethod nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(baseMethod, PsiMethod.class); + while(nextBaseMethod != null) { + String name = nextBaseMethod.isConstructor() ? aClass.getName() : nextBaseMethod.getName(); + if (name != null) { + MethodSignature signature = MethodSignatureUtil.createMethodSignature(name, nextBaseMethod.getParameterList(), nextBaseMethod.getTypeParameterList(), substitutor, nextBaseMethod.isConstructor()); + PsiMethod nextMethod = MethodSignatureUtil.findMethodBySignature(aClass, signature, false); + if (nextMethod != null){ + return nextMethod; + } + } + nextBaseMethod = PsiTreeUtil.getNextSiblingOfType(nextBaseMethod, PsiMethod.class); + } + + return null; + } + + public static void overrideOrImplement(PsiClass psiClass, @NotNull PsiMethod baseMethod) throws IncorrectOperationException { + FileEditorManager fileEditorManager = FileEditorManager.getInstance(baseMethod.getProject()); + + List> prototypes = convert2GenerationInfos(overrideOrImplementMethod(psiClass, baseMethod, false)); + if (prototypes.isEmpty()) return; + + PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseMethod.getContainingClass(), psiClass, PsiSubstitutor.EMPTY); + PsiElement anchor = getDefaultAnchorToOverrideOrImplement(psiClass, baseMethod, substitutor); + List> results = GenerateMembersUtil.insertMembersBeforeAnchor(psiClass, anchor, prototypes); + + PsiFile psiFile = psiClass.getContainingFile(); + Editor editor = fileEditorManager.openTextEditor(new OpenFileDescriptor(psiFile.getProject(), psiFile.getVirtualFile()), false); + if (editor == null) return; + + results.get(0).positionCaret(editor, true); + editor.getScrollingModel().scrollToCaret(ScrollType.CENTER); + } + + @Nullable + public static PsiClass getContextClass(Project project, Editor editor, PsiFile file, boolean allowInterface) { + PsiDocumentManager.getInstance(project).commitAllDocuments(); + + int offset = editor.getCaretModel().getOffset(); + PsiElement element = file.findElementAt(offset); + do { + element = PsiTreeUtil.getParentOfType(element, PsiClass.class); + } + while (element instanceof PsiTypeParameter); + + final PsiClass aClass = (PsiClass)element; + if (aClass instanceof JspClass) return null; + return aClass == null || !allowInterface && aClass.isInterface() ? null : aClass; + } + + public static void overrideOrImplementMethodsInRightPlace(Editor editor1, PsiClass aClass, Collection members, boolean copyJavadoc) { + boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION; + overrideOrImplementMethodsInRightPlace(editor1, aClass, members, copyJavadoc, insert); + } + + public static List overrideOrImplementMethodCandidates(PsiClass aClass, Collection candidatesToImplement, + boolean copyJavadoc) throws IncorrectOperationException { + boolean insert = CodeStyleSettingsManager.getSettings(aClass.getProject()).INSERT_OVERRIDE_ANNOTATION; + return overrideOrImplementMethodCandidates(aClass, candidatesToImplement, copyJavadoc, insert); + } + + public static class MethodSignatureComparator implements Comparator { + // signatures should appear in the order of declaration + public int compare(MethodSignature o1, MethodSignature o2) { + if (o1 instanceof MethodSignatureBackedByPsiMethod && o2 instanceof MethodSignatureBackedByPsiMethod) { + PsiMethod m1 = ((MethodSignatureBackedByPsiMethod)o1).getMethod(); + PsiMethod m2 = ((MethodSignatureBackedByPsiMethod)o2).getMethod(); + PsiClass c1 = m1.getContainingClass(); + PsiClass c2 = m2.getContainingClass(); + if (c1 != null && c2 != null) { + if (c1 == c2) { + final List methods = Arrays.asList(c1.getMethods()); + return methods.indexOf(m1) - methods.indexOf(m2); + } + + if (c1.isInheritor(c2, true)) return -1; + if (c2.isInheritor(c1, true)) return 1; + + return StringUtil.notNullize(c1.getName()).compareTo(StringUtil.notNullize(c2.getName())); + } + return m1.getTextOffset() - m2.getTextOffset(); + } + return 0; + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/AnnotationParameterInfoHandler.java b/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/AnnotationParameterInfoHandler.java index 5c6be31a1c7d..78ea1532d2c1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/AnnotationParameterInfoHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/AnnotationParameterInfoHandler.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.parameterInfo.*; import com.intellij.openapi.project.DumbAware; import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -122,6 +123,6 @@ public class AnnotationParameterInfoHandler implements ParameterInfoHandler consumer) { final PsiElement refElement = p.getElementToSearch(); - if (refElement instanceof PsiAnnotationMethod) { + if (PsiUtil.isAnnotationMethod(refElement)) { PsiMethod method = (PsiMethod)refElement; if (PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(method.getName()) && method.getParameterList().getParametersCount() == 0) { final Query query = ReferencesSearch.search(method.getContainingClass(), p.getScope(), p.isIgnoreAccessScope()); diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 1f4930542e9a..120525e25978 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -690,6 +690,12 @@ public final class PsiUtil extends PsiUtilCore { return file; } + public static boolean isAnnotationMethod(PsiElement element) { + if (!(element instanceof PsiAnnotationMethod)) return false; + PsiClass psiClass = ((PsiAnnotationMethod)element).getContainingClass(); + return psiClass != null && psiClass.isAnnotationType(); + } + private static class TypeParameterIterator implements Iterator { private int myIndex; private PsiTypeParameterListOwner myCurrentOwner; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java index 0187be53a9fa..2b1b117b7553 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java @@ -96,7 +96,7 @@ public class PsiImplUtil { if (resolved != null) { PsiMethod[] methods = ((PsiClass)resolved).getMethods(); for (PsiMethod method : methods) { - if (method instanceof PsiAnnotationMethod && Comparing.equal(method.getName(), attributeName)) { + if (PsiUtil.isAnnotationMethod(method) && Comparing.equal(method.getName(), attributeName)) { return ((PsiAnnotationMethod)method).getDefaultValue(); } } diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/util/InitializerRequirement.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/util/InitializerRequirement.java index b4adcd9c8b15..c4484de9a598 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/util/InitializerRequirement.java +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/util/InitializerRequirement.java @@ -19,6 +19,7 @@ import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiAnnotationMethod; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; +import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.Nullable; enum InitializerRequirement { @@ -32,7 +33,7 @@ enum InitializerRequirement { InitializerRequirement r = NONE_REQUIRED; final PsiMethod[] methods = psiClass.getMethods(); for (PsiMethod method : methods) { - if (method instanceof PsiAnnotationMethod) { + if (PsiUtil.isAnnotationMethod(method)) { final PsiAnnotationMethod annotationMethod = (PsiAnnotationMethod)method; if (annotationMethod.getDefaultValue() == null) { if (PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(annotationMethod.getName())) { From 65643aa190e66b1e7f56cf26539c6f5adc221777 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 23 Jul 2012 15:22:46 +0200 Subject: [PATCH 40/40] IDEA-89114 Unsuitable completion proposals in annotation attribute value --- .../codeInsight/completion/JavaCompletionData.java | 10 +++++++--- .../completion/normal/AnnoParameterValue.java | 3 +++ .../completion/NormalCompletionTest.groovy | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AnnoParameterValue.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java index 55b5acfa6a0a..6cfa6c02dcb6 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java @@ -135,6 +135,7 @@ public class JavaCompletionData extends JavaAwareCompletionData { and(JavaSmartCompletionContributor.INSIDE_EXPRESSION, not(psiElement().afterLeaf(PsiKeyword.CASE)), not(psiElement().afterLeaf(psiElement().withText(".").afterLeaf(PsiKeyword.THIS, PsiKeyword.SUPER))), + not(psiElement().inside(PsiAnnotation.class)), not(START_SWITCH)); public static final AndFilter CLASS_START = new AndFilter( @@ -142,7 +143,8 @@ public class JavaCompletionData extends JavaAwareCompletionData { END_OF_BLOCK, new PatternFilter(psiElement().afterLeaf( or( - psiElement().withoutText(".").inside(psiElement(PsiModifierList.class).withParent(not(psiElement(PsiParameter.class)))), + psiElement().withoutText(".").inside(psiElement(PsiModifierList.class).withParent(not(psiElement(PsiParameter.class)))).andNot( + psiElement().inside(PsiAnnotationParameterList.class)), psiElement().isNull()))) ), new PatternFilter(not(psiElement().afterLeaf("@")))); @@ -486,8 +488,10 @@ public class JavaCompletionData extends JavaAwareCompletionData { } if (EXPR_KEYWORDS.accepts(position)) { - result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); - result.addElement(createKeyword(position, PsiKeyword.NULL)); + if (PsiTreeUtil.getParentOfType(position, PsiAnnotation.class) == null) { + result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE)); + result.addElement(createKeyword(position, PsiKeyword.NULL)); + } result.addElement(createKeyword(position, PsiKeyword.TRUE)); result.addElement(createKeyword(position, PsiKeyword.FALSE)); } diff --git a/java/java-tests/testData/codeInsight/completion/normal/AnnoParameterValue.java b/java/java-tests/testData/codeInsight/completion/normal/AnnoParameterValue.java new file mode 100644 index 000000000000..656e6b8af90b --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AnnoParameterValue.java @@ -0,0 +1,3 @@ +public class Util { + @Foo(val=) void foo(String[] args) { } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 4d58019f3fe1..2a8845b7d0e1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -270,6 +270,20 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase { assert "B" in myFixture.lookupElementStrings } + public void testAnnoParameterValue() throws Exception { + configure() + def strings = myFixture.lookupElementStrings + assert 'AssertionError' in strings + assert !('enum' in strings) + assert !('final' in strings) + assert !('equals' in strings) + assert !('new' in strings) + assert !('null' in strings) + assert !('public' in strings) + assert !('super' in strings) + assert !('null' in strings) + } + public void testAfterInstanceof() throws Exception { configureByFile("AfterInstanceof.java"); assert "A" in myFixture.lookupElementStrings