diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index 98a8e90fcc45..485e3b5b6aac 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -634,7 +634,7 @@ public class GenericsHighlightUtil { type instanceof PsiWildcardType && ((PsiWildcardType)type).getBound() instanceof PsiPrimitiveType) { final PsiElement element = new PsiMatcherImpl(typeElement) .parent(PsiMatchers.hasClass(PsiReferenceParameterList.class)) - .parent(PsiMatchers.hasClass(PsiJavaCodeReferenceElement.class)) + .parent(PsiMatchers.hasClass(PsiJavaCodeReferenceElement.class, PsiNewExpression.class)) .getElement(); if (element == null) return null; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index 47b45ae324ce..4f6dd56b696d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -836,11 +836,15 @@ public class HighlightClassUtil { PsiClass targetClass = aClass.getSuperClass(); if (targetClass == null) return null; PsiExpression qualifier = superCall.getMethodExpression().getQualifierExpression(); - if (qualifier != null && PsiUtil.isInnerClass(targetClass)) { - PsiClass outerClass = targetClass.getContainingClass(); - if (outerClass != null) { - PsiClassType outerType = JavaPsiFacade.getInstance(project).getElementFactory().createType(outerClass); - return HighlightUtil.checkAssignability(outerType, null, qualifier, qualifier); + if (qualifier != null) { + if (PsiUtil.isInnerClass(targetClass)) { + PsiClass outerClass = targetClass.getContainingClass(); + if (outerClass != null) { + PsiClassType outerType = JavaPsiFacade.getInstance(project).getElementFactory().createType(outerClass); + return HighlightUtil.checkAssignability(outerType, null, qualifier, qualifier); + } + } else { + return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, qualifier, "'" + HighlightUtil.formatClass(targetClass) + "' is not an inner class"); } } return null; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index d1a44922714a..9e2e28ba6007 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -1236,6 +1236,12 @@ public class HighlightMethodUtil { } if (classReference != null && aClass.hasModifierProperty(PsiModifier.PROTECTED) && callingProtectedConstructorFromDerivedClass(constructorCall, aClass)) { holder.add(buildAccessProblem(classReference, typeResolveResult, aClass)); + } else if (aClass.isInterface() && constructorCall instanceof PsiNewExpression) { + final PsiReferenceParameterList typeArgumentList = ((PsiNewExpression)constructorCall).getTypeArgumentList(); + if (typeArgumentList.getTypeArguments().length > 0) { + holder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeArgumentList, + "Anonymous class implements interface; cannot have type arguments")); + } } } else { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ExplicitConstructorInvocation.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ExplicitConstructorInvocation.java new file mode 100644 index 000000000000..0bb53bfec1a3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ExplicitConstructorInvocation.java @@ -0,0 +1,42 @@ +class A { + class S { + } + + class C extends S { + C(A c) { + c.super(); + } + C(B b) { + b.super(); + } + } +} +class B extends A {} + +class A1 { + static class S { + } + + class C extends S { + C(A1 c) { + c.super(); + } + } +} + +class C2 { + C2(String c){ + c.super(); + } +} + +class A3 { + class S { + } + + class C extends S { + C(String c) { + c.super(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/wrongPlace.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/wrongPlace.java index 89c407950bfc..8f050d14d909 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/wrongPlace.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/wrongPlace.java @@ -11,4 +11,6 @@ abstract class C { @Anno(nested = {@Anno, @Anno}) void notWrong() { } -} \ No newline at end of file +} + +class B extends @Deprecated Object{} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57275.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57275.java new file mode 100644 index 000000000000..7111b8d872b6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57275.java @@ -0,0 +1,13 @@ +abstract class A { + abstract > void foo(); + + { + foo(); + } +} + +class X{ + > void foo(){ + foo(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57378.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57378.java new file mode 100644 index 000000000000..541079ed4a7c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57378.java @@ -0,0 +1,9 @@ +interface IA { + void foo(T x); + void foo(T x); +} + +abstract class A { + abstract & Iterable> void foo(T x, A y); + abstract > void foo(T x, A y); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57533.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57533.java new file mode 100644 index 000000000000..5bf8674b63d8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57533.java @@ -0,0 +1,6 @@ +class C>>{ + void foo(C x){ + bar(x); + } + > void bar(C x){} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57557.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57557.java new file mode 100644 index 000000000000..56efec17184c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57557.java @@ -0,0 +1,7 @@ +abstract class A { + abstract void foo(Enum x); +} + +class B extends A { + void foo(Enum> x) { } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57563.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57563.java new file mode 100644 index 000000000000..a8cb239a3cd6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57563.java @@ -0,0 +1,7 @@ +class C { + void foo(){} +} + +class D extends C{ + static void foo(){} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57650.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57650.java new file mode 100644 index 000000000000..8f5bb73e6785 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57650.java @@ -0,0 +1,7 @@ +abstract class A { + abstract T foo(); + + { + int x = foo(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57667.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57667.java new file mode 100644 index 000000000000..9f27fa0f14d4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57667.java @@ -0,0 +1,7 @@ +class A { + A(T x) {} + + { + new <String>A(1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57668.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57668.java new file mode 100644 index 000000000000..cc49162b0b8d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57668.java @@ -0,0 +1,17 @@ +class A { + A() {} + + { + new <int>A(); + } +} + +class B { + { + new B<int>(); + B.<int>m(); + } + + void m(){} + +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA67599.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA67599.java new file mode 100644 index 000000000000..fd6c6b4e8aff --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA67599.java @@ -0,0 +1,8 @@ +abstract class A +{ + abstract S bar(); + void foo(A a) + { + int x = a.bar().length; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/TypeArgumentsGivenOnAnonymousClassCreation.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/TypeArgumentsGivenOnAnonymousClassCreation.java new file mode 100644 index 000000000000..33214e28fe0e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/TypeArgumentsGivenOnAnonymousClassCreation.java @@ -0,0 +1,12 @@ +class C +{ + Object x = new D() { }; + Object x1 = new P() { }; + Object x2 = new U() { }; + Object x3 = new I() { }; + interface D{} + abstract class P {} +} + +interface I {} +class U {} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java index 29ac394b4c04..c0532abbcfcc 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java @@ -150,11 +150,21 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase { public void testIDEA89771() throws Exception { doTest(false); } public void testIDEA89801() throws Exception { doTest(false); } public void testIDEA67681() throws Exception { doTest(false); } + public void testIDEA67599() throws Exception { doTest(false); } + public void testIDEA57668() throws Exception { doTest(false); } + public void testIDEA57667() throws Exception { doTest(false); } + public void testIDEA57650() throws Exception { doTest(false); } + public void testIDEA57378() throws Exception { doTest(false); } + public void testIDEA57557() throws Exception { doTest(false); } + public void testIDEA57563() throws Exception { doTest(false); } + public void testIDEA57275() throws Exception { doTest(false); } + public void testIDEA57533() throws Exception { doTest(false); } public void testInconvertibleTypes() throws Exception { doTest(false); } public void testIncompatibleReturnType() throws Exception { doTest(false); } public void testContinueInferenceAfterFirstRawResult() throws Exception { doTest(false); } public void testStaticOverride() throws Exception { doTest(false); } public void testTypeArgumentsGivenOnRawType() throws Exception { doTest(false); } + public void testTypeArgumentsGivenOnAnonymousClassCreation() throws Exception { doTest(false); } public void testJavaUtilCollections_NoVerify() throws Exception { PsiClass collectionsClass = getJavaFacade().findClass("java.util.Collections", GlobalSearchScope.moduleWithLibrariesScope(getModule())); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingTest.java index 9d3b286eb4cc..646805fc7031 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingTest.java @@ -358,4 +358,5 @@ public class LightAdvHighlightingTest extends LightDaemonAnalyzerTestCase { public void testRegexp() throws Exception { doTest(false, false); } public void testUnsupportedFeatures() throws Exception { doTest(false, false); } public void testThisBeforeSuper() throws Exception { doTest(false, false); } + public void testExplicitConstructorInvocation() throws Exception { doTest(false, false); } } diff --git a/java/openapi/src/com/intellij/psi/util/PsiMatchers.java b/java/openapi/src/com/intellij/psi/util/PsiMatchers.java index 32be1cdb136e..72f93672e072 100644 --- a/java/openapi/src/com/intellij/psi/util/PsiMatchers.java +++ b/java/openapi/src/com/intellij/psi/util/PsiMatchers.java @@ -71,7 +71,7 @@ public class PsiMatchers { }; } - public static PsiMatcherExpression hasClass(final Class[] classes) { + public static PsiMatcherExpression hasClass(final Class... classes) { return new PsiMatcherExpression() { @Override public Boolean match(PsiElement element) { diff --git a/platform/indexing-api/src/com/intellij/psi/search/SearchRequestCollector.java b/platform/indexing-api/src/com/intellij/psi/search/SearchRequestCollector.java index ea7f247546a2..22fbfa1b4947 100644 --- a/platform/indexing-api/src/com/intellij/psi/search/SearchRequestCollector.java +++ b/platform/indexing-api/src/com/intellij/psi/search/SearchRequestCollector.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.search; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.PsiReference; @@ -57,6 +58,9 @@ public class SearchRequestCollector { if (searchScope instanceof LocalSearchScope && ((LocalSearchScope)searchScope).getScope().length == 0) { return; } + if (StringUtil.isEmpty(word)) { + return; + } synchronized (lock) { myWordRequests.add(new PsiSearchRequest(searchScope, word, searchContext, caseSensitive, processor)); diff --git a/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java b/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java index 52f0d57f5d4c..3aab2f8df622 100644 --- a/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java +++ b/platform/platform-api/src/com/intellij/ui/MouseDragHelper.java @@ -186,15 +186,10 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe } } - boolean needConsume = true; if (myDetachingMode) { processDragOut(e, draggedTo, (Point)myPressPointScreen.clone(), dragOutStarted); } else { - needConsume = processDrag(e, draggedTo, (Point)myPressPointScreen.clone()); - } - - if (needConsume) { - e.consume(); + processDrag(e, draggedTo, (Point)myPressPointScreen.clone()); } } } @@ -232,14 +227,14 @@ public abstract class MouseDragHelper implements MouseListener, MouseMotionListe return myDragJustStarted; } - protected abstract boolean processDrag(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint); + protected abstract void processDrag(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint); protected boolean isDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint) { return false; } protected void processDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint, boolean justStarted) { - + event.consume(); } private boolean isWithinDeadZone(final MouseEvent e) { diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java index d052c9f999e0..bd8364813049 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/DragHelper.java @@ -71,6 +71,7 @@ class DragHelper extends MouseDragHelper { } else { delegate.processDragOut(event, myDragOutSource); } + event.consume(); } @Override @@ -92,13 +93,13 @@ class DragHelper extends MouseDragHelper { myPressedTabLabel = findLabel(new RelativePoint(event).getPoint(myTabs)); } - protected boolean processDrag(MouseEvent event, Point targetScreenPoint, Point startPointScreen) { - if (!myTabs.isTabDraggingEnabled() || !isDragSource(event)) return false; + protected void processDrag(MouseEvent event, Point targetScreenPoint, Point startPointScreen) { + if (!myTabs.isTabDraggingEnabled() || !isDragSource(event)) return; SwingUtilities.convertPointFromScreen(startPointScreen, myTabs); if (isDragJustStarted()) { - if (myPressedTabLabel == null) return false; + if (myPressedTabLabel == null) return; final Rectangle labelBounds = myPressedTabLabel.getBounds(); @@ -111,7 +112,7 @@ class DragHelper extends MouseDragHelper { myDragOriginalRec.y -= myHoldDelta.height; } else { - if (myDragRec == null) return false; + if (myDragRec == null) return; final Point toPoint = SwingUtilities.convertPoint(event.getComponent(), event.getPoint(), myTabs); @@ -163,7 +164,7 @@ class DragHelper extends MouseDragHelper { headerRec.height += border * 2; myTabs.repaint(headerRec); } - return true; + event.consume(); } private boolean isDragSource(MouseEvent event) { diff --git a/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java b/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java index 856ee05b2227..f54e827e4cf8 100644 --- a/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java +++ b/platform/platform-api/src/com/intellij/util/ui/OptionsDialog.java @@ -15,7 +15,6 @@ */ package com.intellij.util.ui; -import com.intellij.CommonBundle; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import org.jetbrains.annotations.Nullable; @@ -47,7 +46,7 @@ public abstract class OptionsDialog extends DialogWrapper { } public String getDoNotShowMessage() { - return CommonBundle.message("dialog.options.do.not.show"); + return OptionsDialog.this.getDoNotShowMessage(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java index d498e9614e7d..e334aa93b527 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/util/EditorUtil.java @@ -228,7 +228,7 @@ public class EditorUtil { SoftWrapModel softWrapModel = editor.getSoftWrapModel(); List softWraps = softWrapModel.getSoftWrapsForRange(start, maxScanIndex); int startToUse = start; - int x = editor.getPrefixTextWidthInPixels(); + int x = editor.getDocument().getLineNumber(start) == 0 ? editor.getPrefixTextWidthInPixels() : 0; int[] currentColumn = {0}; for (SoftWrap softWrap : softWraps) { // There is a possible case that target column points inside soft wrap-introduced virtual space. diff --git a/platform/platform-impl/src/com/intellij/ui/EditorTextField.java b/platform/platform-impl/src/com/intellij/ui/EditorTextField.java index 6bf4fb24d57e..a7b479fd371b 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorTextField.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorTextField.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -246,7 +246,7 @@ public class EditorTextField extends NonOpaquePanel implements DocumentListener, } } - public void setText(@NotNull final String text) { + public void setText(@Nullable final String text) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { diff --git a/platform/platform-impl/src/com/intellij/ui/Win7TaskBar.java b/platform/platform-impl/src/com/intellij/ui/Win7TaskBar.java index 81387f2556c7..69db0602be92 100644 --- a/platform/platform-impl/src/com/intellij/ui/Win7TaskBar.java +++ b/platform/platform-impl/src/com/intellij/ui/Win7TaskBar.java @@ -81,8 +81,15 @@ class Win7TaskBar { } } + private static boolean ourInitialized = true; static { - initialize(); + try { + initialize(); + } + catch (Throwable e) { + LOG.error(e); + ourInitialized = false; + } } private static void initialize() { @@ -109,7 +116,7 @@ class Win7TaskBar { } static void setProgress(IdeFrame frame, double value, boolean isOk) { - if (ApplicationManager.getApplication().isUnitTestMode()) { + if (!isEnabled()) { return; } @@ -118,8 +125,12 @@ class Win7TaskBar { mySetProgressValue.invokeInt(new Object[]{myInterfacePointer, handle, new WinDef.ULONGLONG((long)(value * 100)), TOTAL_PROGRESS}); } + private static boolean isEnabled() { + return !ApplicationManager.getApplication().isUnitTestMode() && ourInitialized; + } + static void hideProgress(IdeFrame frame) { - if (ApplicationManager.getApplication().isUnitTestMode()) { + if (!isEnabled()) { return; } @@ -127,7 +138,7 @@ class Win7TaskBar { } static void setOverlayIcon(IdeFrame frame, Object icon, boolean dispose) { - if (ApplicationManager.getApplication().isUnitTestMode()) { + if (!isEnabled()) { return; } @@ -141,7 +152,7 @@ class Win7TaskBar { } static Object createIcon(byte[] ico) { - if (ApplicationManager.getApplication().isUnitTestMode()) { + if (!isEnabled()) { return new Object(); } @@ -163,7 +174,7 @@ class Win7TaskBar { } static void attention(IdeFrame frame, boolean critical) { - if (ApplicationManager.getApplication().isUnitTestMode()) { + if (!isEnabled()) { return; } diff --git a/platform/vcs-api/src/com/intellij/vcsUtil/LearningProxy.java b/platform/vcs-api/src/com/intellij/vcsUtil/LearningProxy.java index 6795aa114156..381c43e52b0e 100644 --- a/platform/vcs-api/src/com/intellij/vcsUtil/LearningProxy.java +++ b/platform/vcs-api/src/com/intellij/vcsUtil/LearningProxy.java @@ -28,7 +28,7 @@ import java.util.*; * Date: 10/19/12 * Time: 12:42 PM */ -public abstract class LearningProxy implements InvocationHandler { +public abstract class LearningProxy { private final static Map ourDefaultValues = new HashMap(); static { ourDefaultValues.put("byte", new Byte((byte) 0)); @@ -42,13 +42,25 @@ public abstract class LearningProxy implements InvocationHandler { ourDefaultValues.put("void", null); } private final Set myTrackedMethods; + private final InvocationHandler myLearn; public LearningProxy() { myTrackedMethods = new HashSet(); + myLearn = new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + myTrackedMethods.add(new MethodDescriptor(method)); + final Class returnType = method.getReturnType(); + if (returnType.isPrimitive()) { + return ourDefaultValues.get(returnType.getName()); + } + return null; + } + }; } - protected abstract void onBefore(); - protected abstract void onAfter(); + protected abstract void onBefore() throws E; + protected abstract void onAfter() throws E; public T create(Class clazz, final T t) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() { @@ -58,6 +70,7 @@ public abstract class LearningProxy implements InvocationHandler { if (myTrackedMethods.contains(current)) { try { onBefore(); + method.setAccessible(true); return method.invoke(t, args); } finally { onAfter(); @@ -69,17 +82,7 @@ public abstract class LearningProxy implements InvocationHandler { } public T learn(Class clazz) { - return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this); - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - myTrackedMethods.add(new MethodDescriptor(method)); - final Class returnType = method.getReturnType(); - if (returnType.isPrimitive()) { - return ourDefaultValues.get(returnType.getName()); - } - return null; + return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, myLearn); } private static class MethodDescriptor { diff --git a/platform/vcs-api/src/com/intellij/vcsUtil/PseudoImplementProxy.java b/platform/vcs-api/src/com/intellij/vcsUtil/PseudoImplementProxy.java new file mode 100644 index 000000000000..4d9872589264 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/vcsUtil/PseudoImplementProxy.java @@ -0,0 +1,56 @@ +/* + * 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.vcsUtil; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/22/12 + * Time: 10:58 AM + */ +public class PseudoImplementProxy { + public static T create(final Class theInterface, final Impl implementation) { + checkMethodsExist(theInterface, implementation); + final Class implementationClass = implementation.getClass(); + return (T) Proxy.newProxyInstance(theInterface.getClassLoader(), new Class[]{theInterface}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + final Method implementationClassMethod = + implementationClass.getMethod(method.getName(), method.getParameterTypes()); + implementationClassMethod.setAccessible(true); + return implementationClassMethod.invoke(implementation, args); + } + }); + } + + private static void checkMethodsExist(Class theInterface, Impl implementation) { + final Class implementationClass = implementation.getClass(); + final Method[] methods = theInterface.getDeclaredMethods(); + for (Method method : methods) { + try { + implementationClass.getMethod(method.getName(), method.getParameterTypes()); + } + catch (NoSuchMethodException e) { + throw new UnsupportedOperationException(e); + } + } + } +} diff --git a/plugins/svn4idea/lib/sqljetsrc.zip b/plugins/svn4idea/lib/sqljetsrc.zip new file mode 100644 index 000000000000..3d7a71ef7078 Binary files /dev/null and b/plugins/svn4idea/lib/sqljetsrc.zip differ diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java index 9eaf64b48e2b..1717ea261c9c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/RootsToWorkingCopies.java @@ -90,7 +90,7 @@ public class RootsToWorkingCopies implements VcsListener { @Nullable @CalledInBackground public WorkingCopy getMatchingCopy(final SVNURL url) { - assert ! ApplicationManager.getApplication().isDispatchThread(); + assert (! ApplicationManager.getApplication().isDispatchThread()) || ApplicationManager.getApplication().isUnitTestMode(); if (url == null) return null; final VirtualFile[] roots = ProjectLevelVcsManager.getInstance(myProject).getRootsUnderVcs(SvnVcs.getInstance(myProject)); @@ -111,7 +111,7 @@ public class RootsToWorkingCopies implements VcsListener { @CalledInBackground @Nullable public WorkingCopy getWcRoot(final VirtualFile root) { - assert ! ApplicationManager.getApplication().isDispatchThread(); + assert (! ApplicationManager.getApplication().isDispatchThread()) || ApplicationManager.getApplication().isUnitTestMode(); synchronized (myLock) { if (myUnversioned.contains(root)) return null; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java new file mode 100644 index 000000000000..fea3d259982c --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAbstractWriteOperationLocks.java @@ -0,0 +1,142 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.vcs.changes.FilePathsHelper; +import org.jetbrains.annotations.Nullable; +import org.tmatesoft.sqljet.core.SqlJetException; +import org.tmatesoft.sqljet.core.table.ISqlJetBusyHandler; +import org.tmatesoft.sqljet.core.table.ISqlJetTransaction; +import org.tmatesoft.sqljet.core.table.SqlJetDb; +import org.tmatesoft.svn.core.SVNException; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/19/12 + * Time: 12:09 PM + */ +public abstract class SvnAbstractWriteOperationLocks { + private final long myTimeout; + private final static Map myLockMap = new HashMap(); + private final static Object myLock = new Object(); + private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnAbstractWriteOperationLocks"); + private ISqlJetBusyHandler ourBusyHandler; + + protected SvnAbstractWriteOperationLocks(long timeout) { + myTimeout = timeout; + ourBusyHandler = new ISqlJetBusyHandler() { + @Override + public boolean call(int i) { + try { + Thread.sleep(myTimeout); + } + catch (InterruptedException e) { + // + } + return true; + } + }; + } + + // null if not 1.7+ copy + @Nullable + private Lock getLockObject(File file) throws SVNException { + final boolean directory = file.isDirectory(); + final WorkingCopy wcRoot = getCopy(file, directory); + if (! wcRoot.is17Copy()) return null; + Lock lock; + final String path = FilePathsHelper.convertPath(wcRoot.getFile().getPath()); + synchronized (myLock) { + lock = myLockMap.get(path); + if (lock == null) { + lock = new ReentrantLock(); + myLockMap.put(path, lock); + } + } + return lock; + } + + protected abstract WorkingCopy getCopy(File file, boolean directory) throws SVNException; + + public void lockWrite(final File file) throws SVNException { + final Lock lock = getLockObject(file); + if (lock != null) { + lock.lock(); + } + } + + public void unlockWrite(final File file) throws SVNException { + final Lock lock = getLockObject(file); + if (lock != null) { + lock.unlock(); + } + } + + // would wait until read is available + public void wrapRead(final File file, final Runnable runnable) throws SVNException { + final WorkingCopy copy = getCopy(file, file.isDirectory()); + if (! copy.is17Copy()) { + runnable.run(); + return; + } + + final File root = copy.getFile(); + SqlJetDb open = null; + final boolean run[] = new boolean[1]; + run[0] = false; + try { + open = SqlJetDb.open(SvnUtil.getWcDb(root), false); + open.setBusyHandler(ourBusyHandler); + try { + final SqlJetDb finalOpen = open; + open.runReadTransaction(new ISqlJetTransaction() { + @Override + public Object run(SqlJetDb db) throws SqlJetException { + run[0] = true; + runnable.run(); + return null; + } + }); + } finally { + open.rollback(); + } + } + catch (SqlJetException e) { + LOG.info(e); + if (! run[0]) { + runnable.run(); + } + } + finally { + if (open != null) { + try { + open.close(); + } + catch (SqlJetException e) { + LOG.info(e); + } + } + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java new file mode 100644 index 000000000000..97b87efaa7f9 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnProxies.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn; + +import com.intellij.vcsUtil.LearningProxy; +import org.jetbrains.idea.svn.portable.SVNChangelistClientI; +import org.jetbrains.idea.svn.portable.SvnUpdateClientI; +import org.tmatesoft.svn.core.SVNException; + +import java.io.File; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/19/12 + * Time: 4:22 PM + */ +public class SvnProxies { + private final SvnAbstractWriteOperationLocks myLocks; + private final AtomicReference> myChangelist; + private final AtomicReference> myUpdate; + + public SvnProxies(SvnAbstractWriteOperationLocks locks) { + myLocks = locks; + myChangelist = new AtomicReference>(); + myUpdate = new AtomicReference>(); + } + + public LearningProxy getChangelistsProfessor(final File file) { + if (myChangelist.get() == null) { + final MyLearningProxy proxy = new MyLearningProxy(myLocks, file); + final SVNChangelistClientI learn = proxy.learn(SVNChangelistClientI.class); + try { + learn.addToChangelist(null, null, null, null); + learn.doAddToChangelist(null, null, null, null); + learn.doRemoveFromChangelist(null, null, null); + learn.removeFromChangelist(null, null, null); + } catch (SVNException e) { + //can not occur since methods are not really called + throw new RuntimeException(e); + } + + myChangelist.set(proxy); + } + return myChangelist.get(); + } + + public LearningProxy getUpdateProfessor(final File file) { + if (myUpdate.get() == null) { + final MyLearningProxy proxy = new MyLearningProxy(myLocks, file); + final SvnUpdateClientI learn = proxy.learn(SvnUpdateClientI.class); + try { + learn.doUpdate(null, null, false); + learn.doUpdate(null, null, false, false); + learn.doUpdate((File[]) null, null, null, false, false); + learn.doUpdate((File[]) null, null, null, false, false, false); + learn.doUpdate((File) null, null, null, false, false); + + learn.doSwitch(null, null, null, false); + learn.doSwitch(null, null, null, null, false); + learn.doSwitch(null, null, null, null, false, false); + learn.doSwitch(null, null, null, null, null, false, false); + learn.doSwitch(null, null, null, null, null, false, false, false); + + learn.doCheckout(null, null, null, null, false); + // todo continue + //learn.doCheckout(); + //learn.doCheckout(); + } + catch (SVNException e) { + //can not occur since methods are not really called + throw new RuntimeException(e); + } + myUpdate.set(proxy); + } + return myUpdate.get(); + } + + private static class MyLearningProxy extends LearningProxy { + private final SvnAbstractWriteOperationLocks myLocks; + private final File myFile; + + private MyLearningProxy(SvnAbstractWriteOperationLocks locks, final File file) { + myLocks = locks; + myFile = file; + } + + @Override + protected void onBefore() throws SVNException { + myLocks.lockWrite(myFile); + } + + @Override + protected void onAfter() throws SVNException { + myLocks.unlockWrite(myFile); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java new file mode 100644 index 000000000000..853e995ca219 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnTestWriteOperationLocks.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn; + +import org.tmatesoft.svn.core.SVNException; + +import java.io.File; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/23/12 + * Time: 2:31 PM + */ +public class SvnTestWriteOperationLocks extends SvnAbstractWriteOperationLocks { + private final WorkingCopy myWorkingCopy; + + public SvnTestWriteOperationLocks(WorkingCopy copy) { + super(10); + myWorkingCopy = copy; + } + + @Override + protected WorkingCopy getCopy(File file, boolean directory) throws SVNException { + return myWorkingCopy; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java index 840f34ae23aa..cae785cbf77a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java @@ -583,6 +583,10 @@ public class SvnUtil { } } + public static File getWcDb(final File file) { + return new File(file, ".svn/wc.db"); + } + @Nullable public static File getWcCopyRootIf17(final File file, @Nullable final File upperBound) { File current = file; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java index 01f784cd4875..36e0b08c5a86 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnWriteOperationLocks.java @@ -15,80 +15,36 @@ */ package org.jetbrains.idea.svn; -import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePathImpl; -import com.intellij.openapi.vcs.ProjectLevelVcsManager; -import com.intellij.openapi.vcs.changes.FilePathsHelper; +import com.intellij.openapi.vcs.changes.ChangesUtil; import com.intellij.openapi.vfs.VirtualFile; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import java.io.File; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * Created with IntelliJ IDEA. * User: Irina.Chernushina - * Date: 10/19/12 - * Time: 12:09 PM + * Date: 10/23/12 + * Time: 2:29 PM */ -public class SvnWriteOperationLocks { - private final ProjectLevelVcsManager myVcsManager; - private final Map myLockMap; - private final Object myLock; +public class SvnWriteOperationLocks extends SvnAbstractWriteOperationLocks { + private final RootsToWorkingCopies myRootsToWorkingCopies; - public SvnWriteOperationLocks(final Project project) { - myVcsManager = ProjectLevelVcsManager.getInstance(project); - myLockMap = new HashMap(); - myLock = new Object(); + public SvnWriteOperationLocks(RootsToWorkingCopies rootsToWorkingCopies) { + super(1000); + myRootsToWorkingCopies = rootsToWorkingCopies; } - public void lock(final File file) throws SVNException { - final Lock lock = getLockObject(file); - lock.lock(); - } - - private Lock getLockObject(File file) throws SVNException { - return getLockObject(new FilePathImpl(file, file.isDirectory()), FilePathsHelper.convertPath(file.getPath())); - } - - private Lock getLockObject(VirtualFile file) throws SVNException { - return getLockObject(new FilePathImpl(file), FilePathsHelper.convertPath(file.getPath())); - } - - private Lock getLockObject(FilePathImpl file, final String path) throws SVNException { - final boolean directory = file.isDirectory(); - final VirtualFile root = myVcsManager.getVcsRootFor(file); - if (root == null) { + protected WorkingCopy getCopy(File file, boolean directory) throws SVNException { + final VirtualFile parentOrSelf = ChangesUtil.findValidParentAccurately(new FilePathImpl(file, directory)); + final WorkingCopy wcRoot = myRootsToWorkingCopies.getWcRoot(parentOrSelf); + if (wcRoot == null) { throw new SVNException(SVNErrorMessage.create(directory ? SVNErrorCode.WC_NOT_WORKING_COPY : SVNErrorCode.WC_NOT_FILE)); } - Lock lock; - synchronized (myLock) { - lock = myLockMap.get(path); - if (lock == null) { - lock = new ReentrantLock(); - myLockMap.put(path, lock); - } - } - return lock; - } - - public void unlock(final File file) throws SVNException { - final Lock lock = getLockObject(file); - lock.unlock(); - } - - public void lock(final VirtualFile vf) throws SVNException { - final Lock lock = getLockObject(vf); - lock.lock(); - } - - public void unlock(final VirtualFile vf) throws SVNException { - final Lock lock = getLockObject(vf); - lock.unlock(); + // todo check about externals! + return wcRoot; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java new file mode 100644 index 000000000000..ae04a1aa292e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.history; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/23/12 + * Time: 7:23 PM + */ +public class SvnEditCommitMessageAction extends AnAction { + + // todo we need: + // todo repo url + // todo revision number, and current text + + @Override + public void actionPerformed(AnActionEvent e) { + //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRevisionComment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRevisionComment.java new file mode 100644 index 000000000000..a2f48aafc478 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRevisionComment.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.history; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/23/12 + * Time: 7:24 PM + */ +public class SvnRevisionComment { +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SVNChangelistClientI.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SVNChangelistClientI.java new file mode 100644 index 000000000000..58ee56902b79 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SVNChangelistClientI.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn.portable; + +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNChangelistHandler; + +import java.io.File; +import java.util.Collection; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/19/12 + * Time: 3:50 PM + */ +public interface SVNChangelistClientI extends SvnMarkerInterface { + void getChangeLists(File path, Collection changeLists, SVNDepth depth, ISVNChangelistHandler handler) throws SVNException; + + void getChangeListPaths(Collection changeLists, Collection targets, SVNDepth depth, ISVNChangelistHandler handler) throws SVNException; + + void addToChangelist(File[] paths, SVNDepth depth, String changelist, String[] changelists) throws SVNException; + + void removeFromChangelist(File[] paths, SVNDepth depth, String[] changelists) throws SVNException; + + void doAddToChangelist(File[] paths, SVNDepth depth, String changelist, String[] changelists) throws SVNException; + + void doRemoveFromChangelist(File[] paths, SVNDepth depth, String[] changelists) throws SVNException; + + void doGetChangeListPaths(Collection changeLists, Collection targets, SVNDepth depth, ISVNChangelistHandler handler) throws SVNException; + + void doGetChangeLists(File path, Collection changeLists, SVNDepth depth, ISVNChangelistHandler handler) throws SVNException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnMarkerInterface.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnMarkerInterface.java new file mode 100644 index 000000000000..c043188a990f --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnMarkerInterface.java @@ -0,0 +1,10 @@ +package org.jetbrains.idea.svn.portable; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/19/12 + * Time: 3:48 PM + */ +public interface SvnMarkerInterface { +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnStatusClientI.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnStatusClientI.java index 978e44c9272b..3b5828239abd 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnStatusClientI.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnStatusClientI.java @@ -30,7 +30,7 @@ import java.util.Collection; * Date: 1/24/12 * Time: 9:46 AM */ -public interface SvnStatusClientI { +public interface SvnStatusClientI extends SvnMarkerInterface { long doStatus(File path, boolean recursive, boolean remote, boolean reportAll, boolean includeIgnored, ISVNStatusHandler handler) throws SVNException; long doStatus(File path, boolean recursive, boolean remote, boolean reportAll, boolean includeIgnored, boolean collectParentExternals, ISVNStatusHandler handler) throws SVNException; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnSvnkitChangelistClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnSvnkitChangelistClient.java new file mode 100644 index 000000000000..b57f73139667 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnSvnkitChangelistClient.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 org.jetbrains.idea.svn.portable; + +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.ISVNChangelistHandler; +import org.tmatesoft.svn.core.wc.SVNChangelistClient; + +import java.io.File; +import java.util.Collection; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/19/12 + * Time: 3:51 PM + */ +public class SvnSvnkitChangelistClient implements SVNChangelistClientI { + private final SVNChangelistClient myClient; + + public SvnSvnkitChangelistClient(SVNChangelistClient client) { + myClient = client; + } + + @Override + public void getChangeLists(File path, Collection changeLists, SVNDepth depth, ISVNChangelistHandler handler) throws SVNException { + myClient.getChangeLists(path, changeLists, depth, handler); + } + + @Override + public void getChangeListPaths(Collection changeLists, + Collection targets, + SVNDepth depth, + ISVNChangelistHandler handler) throws SVNException { + myClient.getChangeListPaths(changeLists, targets, depth, handler); + } + + @Override + public void addToChangelist(File[] paths, SVNDepth depth, String changelist, String[] changelists) throws SVNException { + myClient.addToChangelist(paths, depth, changelist, changelists); + } + + @Override + public void removeFromChangelist(File[] paths, SVNDepth depth, String[] changelists) throws SVNException { + myClient.removeFromChangelist(paths, depth, changelists); + } + + @Override + public void doAddToChangelist(File[] paths, SVNDepth depth, String changelist, String[] changelists) throws SVNException { + myClient.doAddToChangelist(paths, depth, changelist, changelists); + } + + @Override + public void doRemoveFromChangelist(File[] paths, SVNDepth depth, String[] changelists) throws SVNException { + myClient.doRemoveFromChangelist(paths, depth, changelists); + } + + @Override + public void doGetChangeListPaths(Collection changeLists, + Collection targets, + SVNDepth depth, + ISVNChangelistHandler handler) throws SVNException { + myClient.doGetChangeListPaths(changeLists, targets, depth, handler); + } + + @Override + public void doGetChangeLists(File path, Collection changeLists, SVNDepth depth, ISVNChangelistHandler handler) + throws SVNException { + myClient.doGetChangeLists(path, changeLists, depth, handler); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnUpdateClientI.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnUpdateClientI.java index f587581f2c31..8eaafa4c8034 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnUpdateClientI.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnUpdateClientI.java @@ -29,7 +29,7 @@ import java.io.File; * Date: 2/1/12 * Time: 11:59 AM */ -public interface SvnUpdateClientI { +public interface SvnUpdateClientI extends SvnMarkerInterface { long doUpdate(File file, SVNRevision revision, boolean recursive) throws SVNException; long doUpdate(File file, SVNRevision revision, boolean recursive, boolean force) throws SVNException; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnWcClientI.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnWcClientI.java index 5aeb4374e145..973058fe3f1b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnWcClientI.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/portable/SvnWcClientI.java @@ -28,7 +28,7 @@ import java.util.Collection; * Date: 1/20/12 * Time: 6:54 PM */ -public interface SvnWcClientI { +public interface SvnWcClientI extends SvnMarkerInterface { void setAddParameters(ISVNAddParameters addParameters); ISVNCommitHandler getCommitHandler(); void setCommitHandler(ISVNCommitHandler handler); diff --git a/plugins/svn4idea/svn4idea-tests.iml b/plugins/svn4idea/svn4idea-tests.iml index 39e6c6bb024d..e95ff454160d 100644 --- a/plugins/svn4idea/svn4idea-tests.iml +++ b/plugins/svn4idea/svn4idea-tests.iml @@ -27,6 +27,15 @@ + + + + + + + + + diff --git a/plugins/svn4idea/svn4idea.iml b/plugins/svn4idea/svn4idea.iml index 58d83821070b..bd4e9cc30efc 100644 --- a/plugins/svn4idea/svn4idea.iml +++ b/plugins/svn4idea/svn4idea.iml @@ -51,7 +51,13 @@ - + + + + + + + diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnBusyOnAddTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnBusyOnAddTest.java index 1ad195c1ac24..a93251736d57 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnBusyOnAddTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnBusyOnAddTest.java @@ -108,8 +108,8 @@ public class SvnBusyOnAddTest extends TestCase { final File ioFile = new File(myWorkingCopyRoot, filename); ioFile.getParentFile().mkdirs(); - SVNWCClient client11 = new SVNWCClient((ISVNRepositoryPool)null, new DefaultSVNOptions()); - client11.doAdd(ioFile.getParentFile(), true, false, true, true); + /*SVNWCClient client11 = new SVNWCClient((ISVNRepositoryPool)null, new DefaultSVNOptions()); + client11.doAdd(ioFile.getParentFile(), true, false, true, true);*/ ioFile.createNewFile(); try { @@ -153,7 +153,7 @@ public class SvnBusyOnAddTest extends TestCase { // } SVNWCClient client = new SVNWCClient((ISVNRepositoryPool)null, new DefaultSVNOptions()); - client.doAdd(ioFile, true, false, true, true); + client.doAdd(ioFile.getParentFile(), true, false, true, true); semaphore.up(); semaphoreWokeUp.waitFor(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java new file mode 100644 index 000000000000..b4949543b66b --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnLockingTest.java @@ -0,0 +1,412 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.svn; + +import com.intellij.idea.Bombed; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.PluginPathManager; +import com.intellij.util.concurrency.Semaphore; +import junit.framework.Assert; +import junit.framework.TestCase; +import org.junit.Before; +import org.tmatesoft.sqljet.core.SqlJetException; +import org.tmatesoft.sqljet.core.table.ISqlJetBusyHandler; +import org.tmatesoft.sqljet.core.table.ISqlJetTransaction; +import org.tmatesoft.sqljet.core.table.SqlJetDb; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; +import org.tmatesoft.svn.core.wc.ISVNRepositoryPool; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 10/23/12 + * Time: 2:27 PM + */ +public class SvnLockingTest extends TestCase { + private File myWorkingCopyRoot; + private SvnTestWriteOperationLocks myLocks; + + @Before + public void setUp() throws Exception { + //PlatformTestCase.initPlatformLangPrefix(); + File pluginRoot = new File(PluginPathManager.getPluginHomePath("svn4idea")); + if (!pluginRoot.isDirectory()) { + // try standalone mode + Class aClass = Svn17TestCase.class; + String rootPath = PathManager.getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class"); + pluginRoot = new File(rootPath).getParentFile().getParentFile().getParentFile(); + } + myWorkingCopyRoot = new File(pluginRoot, "testData/move2unv"); + myLocks = new SvnTestWriteOperationLocks(new WorkingCopy(myWorkingCopyRoot, SVNURL.parseURIEncoded("http://a.b.c"), true)); + } + + @Override + public void tearDown() throws Exception { + } + + public void testPrepare() throws Exception { + final HangInWrite operation1 = new HangInWrite("one", false); + operation1.hang(); + operation1.go(); + } + + public void testPrepareRead() throws Exception { + final HangInRead read = new HangInRead("READ", false); + read.run(); + read.go(); + } + + public void testWritesSequential() throws Exception { + final HangInWrite operation1 = new HangInWrite("one_"); + final HangInWrite operation2 = new HangInWrite("two_"); + + final Thread thread1 = new Thread(operation1); + final Thread thread2 = new Thread(operation2); + try { + thread1.start(); + waitForRunning(operation1); + Assert.assertTrue(operation1.isRunning()); + + thread2.start(); + waitForRunning(operation2); + Assert.assertFalse(operation2.isRunning()); + + operation1.go(); + waitForRunning(operation2); + Assert.assertTrue(operation2.isRunning()); + operation2.go(); + Thread.sleep(10); + } finally { + operation1.stop(); + operation2.stop(); + + thread1.interrupt(); + thread2.interrupt(); + } + } + + @Bombed(year=2020, month = 1,day = 1,description = "waiting for http://issues.tmatesoft.com/issue/SVNKIT-317") + public void testOnlyWrites() throws Exception { + final OnlyWrite operation1 = new OnlyWrite("one"); + final OnlyWrite operation2 = new OnlyWrite("two"); + + final Thread thread1 = new Thread(operation1); + final Thread thread2 = new Thread(operation2); + + try { + thread1.start(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // + } + Assert.assertTrue(operation1.isInsideWrite()); + thread2.start(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // + } + Assert.assertFalse(operation2.isInsideWrite()); + operation1.go(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // + } + Assert.assertTrue(operation2.isInsideWrite()); + operation2.go(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // + } + } finally { + operation1.stop(); + operation2.stop(); + + thread1.interrupt(); + thread2.interrupt(); + } + } + + @Bombed(year=2020, month = 1,day = 1,description = "waiting for http://issues.tmatesoft.com/issue/SVNKIT-317") + public void testReadInBetweenWrites() throws Exception { + final HangInWrite operation1 = new HangInWrite("one1"); + final HangInWrite operation2 = new HangInWrite("two1"); + final HangInRead read = new HangInRead("READ"); + + final Thread thread1 = new Thread(operation1); + final Thread threadRead = new Thread(read); + final Thread thread2 = new Thread(operation2); + + try { + thread1.start(); + waitForRunning(operation1); + Assert.assertTrue(operation1.isRunning()); + + threadRead.start(); + waitForRunning(read); + Assert.assertFalse(read.isRunning()); // not clear why read is allowed to run when write is active, but I've not thought it over so much + + operation1.go(); + waitForRunning(read); + Assert.assertTrue(read.isRunning()); + + thread2.start(); + waitForRunning(operation2); + Assert.assertFalse(operation2.isRunning()); // again, not clear why write is allowed to run when read is active, but I've not thought it over so much + + read.go(); + waitForRunning(operation2); + Assert.assertTrue(operation2.isRunning()); + } finally { + operation1.stop(); + operation2.stop(); + + thread1.interrupt(); + thread2.interrupt(); + threadRead.interrupt(); + } + } + + private void waitForRunning(HangRun operation1) { + int cnt = 10; + while (cnt > 0) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + // + } + if (operation1.isRunning()) break; + -- cnt; + } + } + + public interface HangRun { + void go(); + boolean isRunning(); + void stop(); + } + + private class HangInRead implements Runnable, HangRun { + private final AtomicBoolean myIsRunning; + private final String myName; + private final boolean myWaitFor; + private final Semaphore mySemaphore; + + private HangInRead(String name) { + this(name, true); + } + + private HangInRead(String name, final boolean waitFor) { + myName = name; + myWaitFor = waitFor; + myIsRunning = new AtomicBoolean(false); + mySemaphore = new Semaphore(); + } + + @Override + public void run() { + mySemaphore.down(); + try { + System.out.println("starting read " + myName); + myLocks.wrapRead(myWorkingCopyRoot, new Runnable() { + @Override + public void run() { + myIsRunning.set(true); + System.out.println("inside read " + myName); + final SVNWCClient client = new SVNWCClient((ISVNRepositoryPool)null, new DefaultSVNOptions()); + try { + client.doInfo(myWorkingCopyRoot, SVNRevision.BASE); + } + catch (SVNException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + System.out.println("got status " + myName); + if (myWaitFor) { + mySemaphore.waitFor(); + } + System.out.println("have read " + myName); + } + }); + } + catch (SVNException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + @Override + public void go() { + System.out.println("read going " + myName); + mySemaphore.up(); + } + + @Override + public boolean isRunning() { + System.out.println("read running " + myName + " " + myIsRunning.get()); + return myIsRunning.get(); + } + + @Override + public void stop() { + } + } + + private class OnlyWrite extends HangInWrite { + private OnlyWrite(String name) { + super(name); + } + + @Override + public void run() { + mySemaphore.down(); + operation(); + } + } + + private class HangInWrite implements Runnable, HangRun { + protected final Semaphore mySemaphore; + private final AtomicBoolean myIsRunning; + private final AtomicBoolean myInsideWrite; + private final String myName; + private boolean shouldWait; + private volatile boolean myStopped; + + private HangInWrite(final String name) { + this(name, true); + } + + private HangInWrite(final String name, final boolean shouldWait) { + myName = name; + mySemaphore = new Semaphore(); + this.shouldWait = shouldWait; + myIsRunning = new AtomicBoolean(false); + myInsideWrite = new AtomicBoolean(false); + } + + @Override + public void run() { + try { + hang(); + } + catch (SVNException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + protected void operation() { + System.out.println("TRY OPEN FOR WRITE==="); + SqlJetDb open = null; + try { + open = SqlJetDb.open(SvnUtil.getWcDb(myWorkingCopyRoot), true); + open.setBusyHandler(new ISqlJetBusyHandler() { + @Override + public boolean call(int i) { + if (myStopped) return false; + System.out.println("busy " + myName); + try { + Thread.sleep(10); + } + catch (InterruptedException e) { + // + } + return true; + } + }); + try { + System.out.println("TRY OPEN FOR WRITE " + myName); + open.runWriteTransaction(new ISqlJetTransaction() { + @Override + public Object run(SqlJetDb db) throws SqlJetException { + System.out.println("OPENed FOR WRITE " + myName); + myInsideWrite.set(true); + if (shouldWait) { + mySemaphore.waitFor(); + } + return null; + } + }); + } finally { + myInsideWrite.set(false); + open.rollback(); + } + } + catch (SqlJetException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + finally { + if (open != null) { + try { + open.close(); + } + catch (SqlJetException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + System.out.println("CLOSed FOR WRITE " + myName); + } + } + } + + public boolean isInsideWrite() { + return myInsideWrite.get(); + } + + public void hang() throws SVNException { + System.out.println("starting " + myName); + mySemaphore.down(); + myLocks.lockWrite(myWorkingCopyRoot); + myIsRunning.set(true); + System.out.println("started " + myName); + try { + operation(); + } finally { + myLocks.unlockWrite(myWorkingCopyRoot); + myIsRunning.set(false); + } + } + + public boolean isRunning() { + System.out.println("running " + myName + " " + myIsRunning.get()); + return myIsRunning.get(); + } + + @Override + public void stop() { + myStopped = true; + } + + public void go() { + System.out.println("going " + myName); + mySemaphore.up(); + } + } +}