Merge remote branch 'origin/master'

This commit is contained in:
Dmitry Jemerov
2011-08-22 18:08:50 +02:00
44 changed files with 831 additions and 343 deletions
@@ -2173,6 +2173,13 @@ public class HighlightUtil {
}
if ((resolved instanceof PsiLocalVariable || resolved instanceof PsiParameter) && !(resolved instanceof ImplicitVariable)) {
highlightInfo = HighlightControlFlowUtil.checkVariableMustBeFinal((PsiVariable)resolved, ref);
} else if (resolved instanceof PsiClass) {
if (Comparing.strEqual(((PsiClass)resolved).getQualifiedName(), ((PsiClass)resolved).getName())) {
final PsiElement parent = ref.getParent();
if (parent instanceof PsiImportStaticReferenceElement || parent instanceof PsiImportStaticStatement) {
return HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, refName, JavaErrorMessages.message("cannot.resolve.symbol", refName.getText()));
}
}
}
return highlightInfo;
}
@@ -16,15 +16,20 @@
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.generation.GenerateConstructorHandler;
import com.intellij.psi.PsiClass;
import org.jetbrains.annotations.NotNull;
public class AddDefaultConstructorFix extends AddMethodFix {
public AddDefaultConstructorFix(PsiClass aClass) {
super("public " + aClass.getName() + "() {}", aClass);
super(generateConstructor(aClass), aClass);
setText(QuickFixBundle.message("add.default.constructor.text", aClass.getName()));
}
private static String generateConstructor(PsiClass aClass) {
final String constructorModifier = GenerateConstructorHandler.getConstructorModifier(aClass);
return constructorModifier + (constructorModifier.isEmpty() ? "" : " ") + aClass.getName() + "() {}";
}
@NotNull
public String getFamilyName() {
@@ -267,8 +267,7 @@ public class GenerateConstructorHandler extends GenerateMembersHandlerBase {
}
@Modifier
@Nullable
private static String getConstructorModifier(final PsiClass aClass) {
public static String getConstructorModifier(final PsiClass aClass) {
@Modifier String modifier = PsiModifier.PUBLIC;
if (aClass.hasModifierProperty(PsiModifier.ABSTRACT) && !aClass.isEnum()) {
@@ -280,6 +279,9 @@ public class GenerateConstructorHandler extends GenerateMembersHandlerBase {
else if (aClass.hasModifierProperty(PsiModifier.PRIVATE)) {
modifier = PsiModifier.PRIVATE;
}
else if (aClass.isEnum()) {
modifier = PsiModifier.PRIVATE;
}
return modifier;
}
@@ -24,6 +24,8 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
@@ -65,6 +67,7 @@ public class AddOnDemandStaticImportAction extends PsiElementBaseIntentionAction
return null;
}
PsiClass psiClass = (PsiClass)resolved;
if (Comparing.strEqual(psiClass.getName(), psiClass.getQualifiedName())) return null;
PsiFile file = refExpr.getContainingFile();
if (!(file instanceof PsiJavaFile)) return null;
PsiImportList importList = ((PsiJavaFile)file).getImportList();
@@ -25,7 +25,9 @@ import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
@@ -63,7 +65,7 @@ public class AddSingleMemberStaticImportAction extends PsiElementBaseIntentionAc
PsiClass aClass = ((PsiMember)resolved).getContainingClass();
if (aClass != null && !PsiTreeUtil.isAncestor(aClass, element, true)) {
String qName = aClass.getQualifiedName();
if (qName != null) {
if (qName != null && !Comparing.strEqual(qName, aClass.getName())) {
qName = qName + "." +refExpr.getReferenceName();
if (file instanceof PsiJavaFile) {
PsiImportList importList = ((PsiJavaFile)file).getImportList();
@@ -99,8 +99,11 @@ class CanBeFinalAnnotator extends RefGraphAnnotatorEx {
boolean forReading,
boolean forWriting) {
if (!(refWhat instanceof RefField)) return;
if (!(refFrom instanceof RefMethod) || !((RefMethod)refFrom).isConstructor() || ((PsiField)refWhat.getElement()).hasInitializer() ||
((RefMethod)refFrom).getOwnerClass() != ((RefField)refWhat).getOwnerClass()) {
if (!(refFrom instanceof RefMethod) ||
!((RefMethod)refFrom).isConstructor() ||
((PsiField)refWhat.getElement()).hasInitializer() ||
((RefMethod)refFrom).getOwnerClass() != ((RefField)refWhat).getOwnerClass() ||
((RefField)refWhat).isStatic()) {
if (!referencedFromClassInitializer && forWriting) {
((RefFieldImpl)refWhat).setFlag(false, CAN_BE_FINAL_MASK);
}
@@ -402,19 +402,6 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{
private static void addClassesUsages(PsiPackage aPackage, final Processor<UsageInfo> results, final JavaPackageFindUsagesOptions options) {
final HashSet<PsiFile> filesSet = new HashSet<PsiFile>();
final ArrayList<PsiFile> files = new ArrayList<PsiFile>();
ReferencesSearch.search(new ReferencesSearch.SearchParameters(aPackage, options.searchScope, false, options.fastTrack)).forEach(new ReadActionProcessor<PsiReference>() {
public boolean processInReadAction(final PsiReference psiReference) {
PsiElement ref = psiReference.getElement();
PsiFile file = ref.getContainingFile();
if (filesSet.add(file)) {
files.add(file);
}
return true;
}
});
ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
if (progress != null){
progress.pushState();
@@ -430,17 +417,13 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{
return aClass.getName();
}
})));
progress.checkCanceled();
}
for (PsiFile file : files) {
if (progress != null) {
progress.checkCanceled();
ReferencesSearch.search(new ReferencesSearch.SearchParameters(aClass, options.searchScope, false, options.fastTrack)).forEach(new ReadActionProcessor<PsiReference>() {
public boolean processInReadAction(final PsiReference psiReference) {
return addResult(results, psiReference, options);
}
ReferencesSearch.search(new ReferencesSearch.SearchParameters(aClass, new LocalSearchScope(file), false, options.fastTrack)).forEach(new ReadActionProcessor<PsiReference>() {
public boolean processInReadAction(final PsiReference psiReference) {
return addResult(results, psiReference, options);
}
});
}
});
}
if (progress != null){
@@ -34,6 +34,7 @@ import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.JarFileSystem;
@@ -112,7 +113,7 @@ public class JavaDocumentationProvider implements CodeDocumentationProvider, Ext
boolean trunc = index < text.length();
text = text.substring(0, index);
buffer.append(" = ");
buffer.append(text);
buffer.append(StringUtil.escapeXml(text));
if (trunc) {
buffer.append("...");
}
@@ -0,0 +1,11 @@
import static <error descr="Cannot resolve symbol 'MyConstants'">MyConstants</error>.*;
public class MyClient
{
private int field = MyConstants.CONSTANT;
}
class MyConstants
{
public static final int CONSTANT = 1;
}
@@ -2,36 +2,36 @@
public class a {
int f(int ik) {
if (<error descr="Operator '<' cannot be applied to 'int','null'">1 < null</error>) {}
if (<error descr="Operator '==' cannot be applied to 'null','char'">null == 'c'</error>) {}
if (<error descr="Operator '<' cannot be applied to 'int', 'null'">1 < null</error>) {}
if (<error descr="Operator '==' cannot be applied to 'null', 'char'">null == 'c'</error>) {}
Object o = null;
if (<error descr="Operator '>=' cannot be applied to 'double','java.lang.Object'">1.2 >= o</error>) {}
if (<error descr="Operator '!=' cannot be applied to 'long','java.lang.String'">1L != "null"</error>) {}
if (<error descr="Operator '==' cannot be applied to 'boolean','int'">(1==2) == 3</error>) {}
if (<error descr="Operator '>=' cannot be applied to 'double', 'java.lang.Object'">1.2 >= o</error>) {}
if (<error descr="Operator '!=' cannot be applied to 'long', 'java.lang.String'">1L != "null"</error>) {}
if (<error descr="Operator '==' cannot be applied to 'boolean', 'int'">(1==2) == 3</error>) {}
int i = (<error descr="Operator '+' cannot be applied to 'int','null'">1 + null</error>);
i = <error descr="Operator '/' cannot be applied to 'java.lang.Object','java.lang.Object'">o/o</error>;
i = <error descr="Operator '-' cannot be applied to 'null','double'">null - 1.2</error>;
i = <error descr="Operator '%' cannot be applied to 'boolean','int'">true % 4</error>;
int i = (<error descr="Operator '+' cannot be applied to 'int', 'null'">1 + null</error>);
i = <error descr="Operator '/' cannot be applied to 'java.lang.Object', 'java.lang.Object'">o/o</error>;
i = <error descr="Operator '-' cannot be applied to 'null', 'double'">null - 1.2</error>;
i = <error descr="Operator '%' cannot be applied to 'boolean', 'int'">true % 4</error>;
i = <error descr="Operator '<<' cannot be applied to 'int','java.lang.Object'">i << o</error>;
i = <error descr="Operator '>>' cannot be applied to 'boolean','null'">(i==2) >> null</error>;
i = <error descr="Operator '>>>' cannot be applied to 'int','double'">i >>> 2.2</error>;
i = <error descr="Operator '<<' cannot be applied to 'int', 'java.lang.Object'">i << o</error>;
i = <error descr="Operator '>>' cannot be applied to 'boolean', 'null'">(i==2) >> null</error>;
i = <error descr="Operator '>>>' cannot be applied to 'int', 'double'">i >>> 2.2</error>;
i = <error descr="Operator '&' cannot be applied to 'int','java.lang.Object'">i & o</error>;
i = <error descr="Operator '|' cannot be applied to 'boolean','double'">true | 2.1</error>;
i = <error descr="Operator '&&' cannot be applied to 'int','int'">2 && 3</error>;
i = <error descr="Operator '||' cannot be applied to 'double','long'">3.8 || 2L</error>;
i = <error descr="Operator '||' cannot be applied to 'null','java.lang.Object'">null || o</error>;
i = <error descr="Operator '&' cannot be applied to 'int', 'java.lang.Object'">i & o</error>;
i = <error descr="Operator '|' cannot be applied to 'boolean', 'double'">true | 2.1</error>;
i = <error descr="Operator '&&' cannot be applied to 'int', 'int'">2 && 3</error>;
i = <error descr="Operator '||' cannot be applied to 'double', 'long'">3.8 || 2L</error>;
i = <error descr="Operator '||' cannot be applied to 'null', 'java.lang.Object'">null || o</error>;
<error descr="Operator '|' cannot be applied to 'int','null'">i |= null</error>;
<error descr="Operator '|' cannot be applied to 'int', 'null'">i |= null</error>;
double d = 0;
<error descr="Operator '&' cannot be applied to 'double','int'">d &= i</error>;
<error descr="Operator '/' cannot be applied to 'java.lang.Object','int'">o /= 3</error>;
<error descr="Operator '&' cannot be applied to 'double', 'int'">d &= i</error>;
<error descr="Operator '/' cannot be applied to 'java.lang.Object', 'int'">o /= 3</error>;
String sss2 = <error descr="Operator '+' cannot be applied to 'java.lang.String','void'">"" + fvoid()</error>;
int sss1 = <error descr="Operator '+' cannot be applied to 'void','int'">fvoid() + 2</error>;
String sss2 = <error descr="Operator '+' cannot be applied to 'java.lang.String', 'void'">"" + fvoid()</error>;
int sss1 = <error descr="Operator '+' cannot be applied to 'void', 'int'">fvoid() + 2</error>;
int ia[] = null;
boolean b = 1==3 || 3 < '4' && (1>3.5) == (o == null) || false || (o == "d");
@@ -44,7 +44,7 @@ public class a {
ia[~i | (i+=(!b?2:i))] -= i + 3.3;
// Object += String
<error descr="Operator '+' cannot be applied to 'java.lang.Object','java.lang.String'">o += o + "string"</error>;
<error descr="Operator '+' cannot be applied to 'java.lang.Object', 'java.lang.String'">o += o + "string"</error>;
return 0;
}
@@ -4,7 +4,7 @@ public class Autoboxing {
}
public boolean compare(Short s, Integer i) {
return <error descr="Operator '==' cannot be applied to 'java.lang.Integer','java.lang.Short'">i == s</error>; //comparing as references
return <error descr="Operator '==' cannot be applied to 'java.lang.Integer', 'java.lang.Short'">i == s</error>; //comparing as references
}
void f(Integer i) {
@@ -15,7 +15,7 @@ public class Autoboxing {
{
Object data = 1;
boolean is1 = <error descr="Operator '==' cannot be applied to 'java.lang.Object','int'">data == 1</error>;
boolean is1 = <error descr="Operator '==' cannot be applied to 'java.lang.Object', 'int'">data == 1</error>;
}
//IDEADEV-5549: Short and double are convertible
@@ -78,7 +78,7 @@ class mylist<T> {}
class myAList<T> extends mylist<T> {
{
mylist<String> l = <error descr="Inconvertible types; cannot cast 'myAList<java.lang.Integer>' to 'mylist<java.lang.String>'">(mylist<String>) new myAList<Integer>()</error>;
boolean b = <error descr="Operator '==' cannot be applied to 'myAList<java.lang.Integer>','myAList<java.lang.String>'">new myAList<Integer>() == new myAList<String>()</error>;
boolean b = <error descr="Operator '==' cannot be applied to 'myAList<java.lang.Integer>', 'myAList<java.lang.String>'">new myAList<Integer>() == new myAList<String>()</error>;
if (l instanceof <error descr="Illegal generic type for instanceof">myAList<String></error>);
Object o = new Object();
@@ -1,5 +1,5 @@
// "Add constructor parameter" "true"
class A {
public class A {
private int field;
public A(int field) {
@@ -1,5 +1,5 @@
// "Add constructor parameter" "true"
class A {
public class A {
private final A field;
public A(A field) {
@@ -0,0 +1,10 @@
// "Add constructor parameter" "true"
public enum MyEnum
{
VALUE_ONE(myField);
private final int myField;
private MyEnum(int myField) {
this.myField = myField;
}
}
@@ -1,5 +1,5 @@
// "Add constructor parameters" "true"
class A {
public class A {
private final int field;
private final Object o;
private final Runnable runnable;
@@ -1,4 +1,4 @@
// "Add constructor parameter" "true"
class A {
public class A {
private int <caret>field;
}
@@ -1,4 +1,4 @@
// "Add constructor parameter" "true"
class A {
public class A {
private final A field;<caret>
}
@@ -0,0 +1,6 @@
// "Add constructor parameter" "true"
public enum MyEnum
{
VALUE_ONE;
private final int myFi<caret>eld;
}
@@ -1,5 +1,5 @@
// "Add constructor parameters" "true"
class A {
public class A {
private final int <caret>field;
private final Object o;
private final Runnable runnable;
@@ -7,6 +7,6 @@ public enum Operation {
abstract int eval(int x, int y);
Operation() {
private Operation() {
}
}
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>Foo.java</file>
<line>2</line>
<description>final</description>
</problem>
</problems>
@@ -0,0 +1,13 @@
public final class Foo {
private static Object f1 = new Object(); // Can be final but unused
private static Object object; // can't be final
Foo() {
object = new Object();
}
public static void main(String[] args) {
System.out.println(Foo.f1);
System.out.println(Foo.object);
}
}
@@ -2,8 +2,16 @@ package com.intellij.codeInsight.daemon;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.codeInsight.hint.ImplementationViewComponent;
import com.intellij.openapi.util.Disposer;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.junit.Assert;
import java.util.*;
/**
* User: anna
@@ -72,4 +80,99 @@ public class ImplementationsViewTest extends LightCodeInsightFixtureTestCase {
" return \"text\";\n" +
" }", newText);
}
public void testInnerClasses() {
myFixture.configureByText("a.java", "abstract class AF<caret>oo{\n" +
" abstract boolean aaa();\n" +
" static class AFoo1 extends AFoo {\n" +
" @Override\n" +
" boolean aaa() {\n" +
" return false;\n" +
" }\n" +
" }\n" +
" static class AFoo3 extends AFoo {\n" +
" @Override\n" +
" boolean aaa() {\n" +
" return false;\n" +
" }\n" +
" }\n" +
" static class AFoo2 extends AFoo {\n" +
" @Override\n" +
" boolean aaa() {\n" +
" return false;\n" +
" }\n" +
" }\n" +
" \n" +
"}");
PsiClass psiClass =
(PsiClass)TargetElementUtilBase.findTargetElement(myFixture.getEditor(), TargetElementUtilBase.getInstance().getAllAccepted());
assert psiClass != null;
final Collection<PsiClass> classes = ClassInheritorsSearch.search(psiClass).findAll();
List<PsiClass> all = new ArrayList<PsiClass>();
all.add(psiClass);
all.addAll(classes);
final ImplementationViewComponent component =
new ImplementationViewComponent(all.toArray(new PsiElement[all.size()]), 0);
try {
final String[] visibleFiles = component.getVisibleFiles();
Assert.assertArrayEquals(Arrays.toString(visibleFiles),
new String[]{"a.java (AFoo)", "a.java (AFoo1 in AFoo)", "a.java (AFoo3 in AFoo)", "a.java (AFoo2 in AFoo)"}, visibleFiles);
}
finally {
component.removeNotify();
}
}
public void testMethodsInInnerClasses() {
myFixture.configureByText("a.java", "abstract class AFoo{\n" +
" abstract boolean a<caret>aa();\n" +
" static class AFoo1 extends AFoo {\n" +
" @Override\n" +
" boolean aaa() {\n" +
" return false;\n" +
" }\n" +
" }\n" +
" static class AFoo3 extends AFoo {\n" +
" @Override\n" +
" boolean aaa() {\n" +
" return false;\n" +
" }\n" +
" }\n" +
" static class AFoo2 extends AFoo {\n" +
" @Override\n" +
" boolean aaa() {\n" +
" return false;\n" +
" }\n" +
" }\n" +
" \n" +
"}");
PsiMethod psiMethod =
(PsiMethod)TargetElementUtilBase.findTargetElement(myFixture.getEditor(), TargetElementUtilBase.getInstance().getAllAccepted());
assert psiMethod != null;
final Collection<PsiMethod> methods = OverridingMethodsSearch.search(psiMethod).findAll();
List<PsiMethod> all = new ArrayList<PsiMethod>();
all.add(psiMethod);
all.addAll(methods);
//make sure they are in predefined order
Collections.sort(all, new Comparator<PsiMethod>() {
@Override
public int compare(PsiMethod o1, PsiMethod o2) {
return o1.getContainingClass().getQualifiedName()
.compareTo(o2.getContainingClass().getQualifiedName());
}
});
final ImplementationViewComponent component =
new ImplementationViewComponent(all.toArray(new PsiElement[all.size()]), 0);
try {
final String[] visibleFiles = component.getVisibleFiles();
Assert.assertArrayEquals(Arrays.toString(visibleFiles),
new String[]{"a.java (AFoo)", "a.java (AFoo1 in AFoo)", "a.java (AFoo2 in AFoo)", "a.java (AFoo3 in AFoo)"}, visibleFiles);
}
finally {
component.removeNotify();
}
}
}
@@ -175,6 +175,7 @@ public class LightAdvHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testIDEADEV13249() throws Exception { doTest(false, false); }
public void testIDEADEV11919() throws Exception { doTest(false, false); }
public void testMethodCannotBeApplied() throws Exception { doTest(false, false); }
public void testDefaultPackageClassInStaticImport() throws Exception { doTest(false, false); }
public void testUnusedParamsOfPublicMethod() throws Exception { doTest(true, false); }
@@ -82,6 +82,10 @@ public class CanBeFinalTest extends InspectionTestCase {
doTest();
}
public void teststaticFields() throws Exception {
doTest();
}
public void testSCR7737() throws Exception {
CanBeFinalInspection tool = new CanBeFinalInspection();
tool.REPORT_CLASSES = false;
@@ -200,10 +200,29 @@ public abstract class VirtualFile extends UserDataHolderBase implements Modifica
*/
public abstract boolean isDirectory();
/**
* Checks whether this file is a symbolic link.
*
* @since 11.0
* @return <code>true</code> if this file is a symbolic link, <code>false</code> otherwise
*/
public boolean isSymLink() {
return false;
}
/**
* Attempts to resolve a symbolic link represented by this file and returns link target.
*
* @since 11.0
* @return <code>this</code> if the file isn't a symbolic link;
* instance of <code>VirtualFile</code> if the link was successfully resolved;
* <code>null</code> otherwise
*/
@Nullable
public VirtualFile getRealFile() {
return this;
}
/**
* Checks whether this <code>VirtualFile</code> is valid. File can be invalidated either by deleting it or one of its
* parents with {@link #delete} method or by an external change.
@@ -38,6 +38,7 @@ import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.presentation.java.SymbolPresentationUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.SideBorder;
@@ -49,6 +50,7 @@ import com.intellij.usages.UsageViewManager;
import com.intellij.usages.UsageViewPresentation;
import com.intellij.util.PairFunction;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import javax.swing.border.CompoundBorder;
@@ -57,7 +59,9 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ImplementationViewComponent extends JPanel {
@NonNls private static final String TEXT_PAGE_KEY = "Text";
@@ -87,7 +91,7 @@ public class ImplementationViewComponent extends JPanel {
}
public boolean hasElementsToShow() {
return myElements.length > 0;
return myElements != null && myElements.length > 0;
}
private static class FileDescriptor {
@@ -96,7 +100,7 @@ public class ImplementationViewComponent extends JPanel {
public FileDescriptor(PsiFile file, PsiElement element) {
myFile = file;
myElementPresentation = element instanceof PsiNamedElement ? ((PsiNamedElement)element).getName() : null;
myElementPresentation = SymbolPresentationUtil.getSymbolPresentableText(element);
}
public String getPresentableName(VirtualFile vFile) {
@@ -237,6 +241,17 @@ public class ImplementationViewComponent extends JPanel {
});
}
@TestOnly
public String[] getVisibleFiles() {
final ComboBoxModel model = myFileChooser.getModel();
String[] result = new String[model.getSize()];
for (int i = 0; i < model.getSize(); i++) {
FileDescriptor o = (FileDescriptor)model.getElementAt(i);
result[i] = o.getPresentableName(o.myFile.getVirtualFile());
}
return result;
}
public void update(final PsiElement[] elements, final int index) {
update(elements, new PairFunction<PsiElement[], List<FileDescriptor>, Boolean>() {
@Override
@@ -296,11 +311,17 @@ public class ImplementationViewComponent extends JPanel {
private static void update(final PsiElement[] elements, final PairFunction<PsiElement[], List<FileDescriptor>, Boolean> fun) {
List<PsiElement> candidates = new ArrayList<PsiElement>(elements.length);
List<FileDescriptor> files = new ArrayList<FileDescriptor>(elements.length);
final Set<String> names = new HashSet<String>();
for (PsiElement element : elements) {
if (element instanceof PsiNamedElement) {
names.add(((PsiNamedElement)element).getName());
}
}
for (PsiElement element : elements) {
PsiFile file = getContainingFile(element);
if (file == null) continue;
final PsiElement parent = element.getParent();
files.add(new FileDescriptor(file, parent == file ? element : parent));
files.add(new FileDescriptor(file, names.size() > 1 || parent == file ? element : parent));
candidates.add(element.getNavigationElement());
}
@@ -40,10 +40,31 @@ public abstract class LocalFileSystem extends NewVirtualFileSystem {
return LocalFileSystemHolder.ourInstance;
}
/**
* Checks whether given file is a symbolic link.
*
* @param file a file to check.
* @return <code>true</code> if the file is a symbolic link, <code>false</code> otherwise
* @since 11.0
*/
public boolean isSymLink(@NotNull final VirtualFile file) {
return false;
}
/**
* Attempts to resolve a symbolic link represented by given file and returns link target.
*
* @since 11.0
* @param file a file to resolve.
* @return <code>this</code> if the file isn't a symbolic link;
* instance of <code>VirtualFile</code> if the link was successfully resolved;
* <code>null</code> otherwise
*/
@Nullable
public VirtualFile getRealFile(@NotNull final VirtualFile file) {
return file;
}
@Nullable
public abstract VirtualFile findFileByIoFile(File file);
@@ -26,6 +26,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.SymLinkUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.ManagingFS;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
@@ -214,13 +215,8 @@ public class FileWatcher {
}
private static String getCanonicalPath(final String path) {
try {
return new File(path).getCanonicalPath();
}
catch (IOException e) {
LOG.warn(e.getMessage() + ": " + path);
return path;
}
final String realPath = SymLinkUtil.resolveSymLink(path);
return realPath != null ? realPath : path;
}
private boolean isAlive() {
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.SymLinkUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.ex.VirtualFileManagerEx;
@@ -170,22 +171,21 @@ public abstract class LocalFileSystemBase extends LocalFileSystem {
}
}
File ioFile = convertToIOFile(file);
if (file.isSymLink() && isRecursiveSymLink(ioFile)) {
if (isInvalidSymLink(file)) {
return ArrayUtil.EMPTY_STRING_ARRAY;
}
final File ioFile = convertToIOFile(file);
final String[] names = ioFile.list();
return names != null ? names : ArrayUtil.EMPTY_STRING_ARRAY;
}
protected static boolean isRecursiveSymLink(File ioFile) {
try {
if (FileUtil.isAncestor(ioFile.getCanonicalFile(), ioFile, true)) return true;
}
catch (IOException ignore) {
}
return false;
protected static boolean isInvalidSymLink(@NotNull final VirtualFile file) {
if (!file.isSymLink()) return false;
final VirtualFile realFile = file.getRealFile();
return realFile == null ||
realFile == file ||
FileUtil.isAncestor(convertToIOFile(realFile), convertToIOFile(file), true);
}
@NotNull
@@ -22,6 +22,7 @@ import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.SymLinkUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
@@ -471,6 +472,12 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap
return SymLinkUtil.isSymLink(file.getPath());
}
@Override
public VirtualFile getRealFile(@NotNull final VirtualFile file) {
final String realPath = SymLinkUtil.resolveSymLink(file.getPath());
return realPath != null ? findFileByPath(realPath) : null;
}
public boolean isWritable(@NotNull final VirtualFile file) {
if (myNativeFileSystem == null) return super.isWritable(file);
else return myNativeFileSystem.isWritable(file);
@@ -1,141 +0,0 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vfs.impl.local;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.ArrayUtil;
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 java.io.File;
import java.lang.reflect.Method;
// todo[r.sh] use NIO2 API after migration to JDK 7
public class SymLinkUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.impl.local.SymLinkUtil");
@Nullable
private static final Mediator ourMediator;
static {
Mediator mediator = null;
if (SystemInfo.isJavaVersionAtLeast("1.7")) {
try {
mediator = new Jdk7MediatorImpl();
mediator.isSymLink("/"); // quick test
}
catch (Exception e) {
LOG.error(e);
mediator = null;
}
}
if (mediator == null && (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris)) {
try {
mediator = new JnaMediatorImpl();
mediator.isSymLink("/"); // quick test
}
catch (Exception e) {
LOG.error(e);
mediator = null;
}
}
ourMediator = mediator;
}
private SymLinkUtil() { }
public static boolean isSymLink(@NotNull final File file) {
return isSymLink(file.getAbsolutePath());
}
public static boolean isSymLink(@NotNull final String path) {
try {
return ourMediator != null && ourMediator.isSymLink(path);
}
catch (Exception e) {
LOG.error(e);
return false;
}
}
private interface Mediator {
boolean isSymLink(@NotNull final String path) throws Exception;
}
private static class Jdk7MediatorImpl implements Mediator {
private final Method myGetDefault;
private final Method myGetPath;
private final Method myIsSymbolicLink;
private Jdk7MediatorImpl() throws Exception {
myGetDefault = Class.forName("java.nio.file.FileSystems").getMethod("getDefault");
myGetPath = Class.forName("java.nio.file.FileSystem").getMethod("getPath", String.class, String[].class);
myIsSymbolicLink = Class.forName("java.nio.file.Files").getMethod("isSymbolicLink", Class.forName("java.nio.file.Path"));
}
@Override
public boolean isSymLink(@NotNull final String path) throws Exception {
final Object fileSystem = myGetDefault.invoke(null);
final Object pathObj = myGetPath.invoke(fileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY);
return (Boolean)myIsSymbolicLink.invoke(null, pathObj);
}
}
// thanks to SVNKit for the idea
@SuppressWarnings("OctalInteger")
private static class JnaMediatorImpl implements Mediator {
private interface LibC extends Library {
int S_MASK = 0177777;
int S_IFLNK = 0120000;
int lstat(String path, Pointer stat);
int __lxstat64(int ver, String path, Pointer stat);
}
private final LibC myLibC;
private final Memory mySharedMem;
private final int myOffset;
private JnaMediatorImpl() throws Exception {
myLibC = (LibC)Native.loadLibrary("c", LibC.class);
mySharedMem = new Memory(512);
myOffset = SystemInfo.isLinux ? (SystemInfo.is32Bit ? 16 : 24) :
SystemInfo.isMac ? 8 :
SystemInfo.isSolaris ? (SystemInfo.is32Bit ? 20 : 16) :
-1;
if (myOffset < 0) throw new IllegalStateException("Unsupported OS: " + SystemInfo.OS_NAME);
}
@Override
public synchronized boolean isSymLink(@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(myOffset) : mySharedMem.getShort(myOffset)) & LibC.S_MASK;
return (mode & LibC.S_IFLNK) == LibC.S_IFLNK;
}
else {
//LOG.warn("lstat(" + path + "): " + res);
return false;
}
}
}
}
@@ -425,4 +425,10 @@ public abstract class VirtualFileSystemEntry extends NewVirtualFile {
final NewVirtualFileSystem fs = getFileSystem();
return fs instanceof LocalFileSystem && ((LocalFileSystem)fs).isSymLink(this);
}
@Override
public VirtualFile getRealFile() {
final NewVirtualFileSystem fs = getFileSystem();
return fs instanceof LocalFileSystem ? ((LocalFileSystem)fs).getRealFile(this) : super.getRealFile();
}
}
@@ -51,48 +51,79 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
private Project myProject;
private static final Callback SHOULD_ENABLE_URL = new Callback() {
public boolean callback(ID self, String selector, ID panel, ID url) {
return true;
}
};
private static final Callback SHOULD_SHOW_FILENAME_CALLBACK = new Callback() {
public boolean callback(ID self, String selector, ID panel, ID filename) {
if (filename == null || filename.intValue() == 0) return false;
final String fileName = Foundation.toStringViaUTF8(filename);
final VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(fileName);
return virtualFile != null && (virtualFile.isDirectory() || getDescriptor().isFileSelectable(virtualFile));
public boolean callback(ID self, String selector, ID panel, ID filename) {
if (filename == null || filename.intValue() == 0) return false;
final String fileName = Foundation.toStringViaUTF8(filename);
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
LocalFileSystem.getInstance().refreshAndFindFileByPath(fileName);
}
});
}
};
catch (Exception e) {
return false;
}
final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(fileName);
return virtualFile != null && (virtualFile.isDirectory() || getDescriptor().isFileSelectable(virtualFile));
}
};
private static final Callback IS_VALID_FILENAME_CALLBACK = new Callback() {
public boolean callback(ID self, String selector, ID panel, ID filename) {
if (filename == null || filename.intValue() == 0) return false;
final String fileName = Foundation.toStringViaUTF8(filename);
final VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(fileName);
return virtualFile != null && (!virtualFile.isDirectory() || getDescriptor().isFileSelectable(virtualFile));
public boolean callback(ID self, String selector, ID panel, ID filename) {
if (filename == null || filename.intValue() == 0) return false;
final String fileName = Foundation.toStringViaUTF8(filename);
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
LocalFileSystem.getInstance().refreshAndFindFileByPath(fileName);
}
});
}
};
catch (Exception e) {
return false;
}
final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(fileName);
return virtualFile != null && (!virtualFile.isDirectory() || getDescriptor().isFileSelectable(virtualFile));
}
};
private static final Callback OPEN_PANEL_DID_END = new Callback() {
public void callback(ID self, String selector, ID openPanelDidEnd, ID returnCode, ID contextInfo) {
processResult(returnCode, openPanelDidEnd);
public void callback(ID self, String selector, ID openPanelDidEnd, ID returnCode, ID contextInfo) {
processResult(returnCode, openPanelDidEnd);
try {
if (myResultFiles != null) {
final VirtualFile[] chosenFiles = VfsUtil.toVirtualFileArray(myResultFiles);
final MacFileChooserCallback callback = mySheetCallback;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
callback.onChosen(chosenFiles);
}
});
}
try {
if (myResultFiles != null) {
final VirtualFile[] chosenFiles = VfsUtil.toVirtualFileArray(myResultFiles);
final MacFileChooserCallback callback = mySheetCallback;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
callback.onChosen(chosenFiles);
}
});
}
finally {
myFileChooserActive = false;
myResultFiles = null;
mySheetCallback = null;
}
Foundation.cfRelease(self);
}
};
finally {
myFileChooserActive = false;
myResultFiles = null;
mySheetCallback = null;
}
Foundation.cfRelease(self);
}
};
private static final Callback MAIN_THREAD_RUNNABLE = new Callback() {
@@ -105,11 +136,13 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
invoke(chooser, "setAllowsMultipleSelection:", myChooserDescriptor.isChooseMultiple());
invoke(chooser, "setTreatsFilePackagesAsDirectories:", myChooserDescriptor.isChooseFolders());
//invoke(chooser, "setCanCreateDirectories:", true);
if (Foundation.isClassRespondsToSelector(Foundation.getClass("NSOpenPanel"), Foundation.createSelector("_setIncludeNewFolderButton:"))) {
if (Foundation
.isClassRespondsToSelector(Foundation.getClass("NSOpenPanel"), Foundation.createSelector("_setIncludeNewFolderButton:"))) {
invoke(chooser, "_setIncludeNewFolderButton:", true);
}
final Object showHiddenFiles = myChooserDescriptor.getUserData(MacFileChooserDialog.NATIVE_MAC_FILE_CHOOSER_SHOW_HIDDEN_FILES_ENABLED.getName());
final Object showHiddenFiles =
myChooserDescriptor.getUserData(MacFileChooserDialog.NATIVE_MAC_FILE_CHOOSER_SHOW_HIDDEN_FILES_ENABLED.getName());
if (Registry.is("ide.mac.filechooser.showhidden.files")
|| Boolean.TRUE.equals(showHiddenFiles)) {
if (Foundation.isClassRespondsToSelector(Foundation.getClass("NSOpenPanel"), Foundation.createSelector("setShowsHiddenFiles:"))) {
@@ -124,12 +157,13 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
final String toSelectPath = toSelect == null || toSelect.intValue() == 0 ? null : Foundation.toStringViaUTF8(toSelect);
final VirtualFile toSelectFile = toSelectPath == null ? null : LocalFileSystem.getInstance().findFileByPath(toSelectPath);
if (toSelectFile != null) {
if (toSelectFile.isDirectory()) {
directory = toSelect;
} else {
directory = Foundation.cfString(toSelectFile.getParent().getPath());
file = Foundation.cfString(toSelectFile.getName());
}
if (toSelectFile.isDirectory()) {
directory = toSelect;
}
else {
directory = Foundation.cfString(toSelectFile.getParent().getPath());
file = Foundation.cfString(toSelectFile.getName());
}
}
if (mySheetCallback != null) {
@@ -138,7 +172,8 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
String activeWindowTitle = null;
if (activeWindow instanceof Frame) {
activeWindowTitle = ((Frame)activeWindow).getTitle();
} else if (activeWindow instanceof JDialog) {
}
else if (activeWindow instanceof JDialog) {
activeWindowTitle = ((JDialog)activeWindow).getTitle();
}
@@ -148,7 +183,8 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
directory, file, null, focusedWindow, self, Foundation.createSelector("openPanelDidEnd:returnCode:contextInfo:"), null);
}
}
} else {
}
else {
final ID result = invoke(chooser, "runModalForDirectory:file:", directory, file);
processResult(result, chooser);
}
@@ -187,14 +223,22 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
static {
final ID delegateClass = Foundation.registerObjcClass(Foundation.getClass("NSObject"), "NSOpenPanelDelegate_");
if (!Foundation.addMethod(delegateClass, Foundation.createSelector("panel:shouldShowFilename:"), SHOULD_SHOW_FILENAME_CALLBACK, "B*"))
if (!Foundation.addMethod(delegateClass, Foundation.createSelector("panel:shouldShowFilename:"), SHOULD_SHOW_FILENAME_CALLBACK, "B*")) {
throw new RuntimeException("Unable to add method to objective-c delegate class!");
if (!Foundation.addMethod(delegateClass, Foundation.createSelector("panel:isValidFilename:"), IS_VALID_FILENAME_CALLBACK, "B*"))
}
if (!Foundation.addMethod(delegateClass, Foundation.createSelector("panel:isValidFilename:"), IS_VALID_FILENAME_CALLBACK, "B*")) {
throw new RuntimeException("Unable to add method to objective-c delegate class!");
if (!Foundation.addMethod(delegateClass, Foundation.createSelector("showOpenPanel:"), MAIN_THREAD_RUNNABLE, "v*"))
}
if (!Foundation.addMethod(delegateClass, Foundation.createSelector("showOpenPanel:"), MAIN_THREAD_RUNNABLE, "v*")) {
throw new RuntimeException("Unable to add method to objective-c delegate class!");
if (!Foundation.addMethod(delegateClass, Foundation.createSelector("openPanelDidEnd:returnCode:contextInfo:"), OPEN_PANEL_DID_END, "v*i^void"))
}
if (!Foundation
.addMethod(delegateClass, Foundation.createSelector("openPanelDidEnd:returnCode:contextInfo:"), OPEN_PANEL_DID_END, "v*i")) {
throw new RuntimeException("Unable to add method to objective-c delegate class!");
}
if (!Foundation.addMethod(delegateClass, Foundation.createSelector("panel:shouldEnableURL:"), SHOULD_ENABLE_URL, "B@@")) {
throw new RuntimeException("Unable to add method to objective-c delegate class!");
}
Foundation.registerObjcClassPair(delegateClass);
}
@@ -248,8 +292,8 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
}
public void chooseWithSheet(@Nullable final VirtualFile toSelect, @Nullable final Project project,
@NotNull final MacFileChooserCallback callback) {
assert !myFileChooserActive: "Current native file chooser should finish before next usage!";
@NotNull final MacFileChooserCallback callback) {
assert !myFileChooserActive : "Current native file chooser should finish before next usage!";
mySheetCallback = callback;
SwingUtilities.invokeLater(new Runnable() {
@@ -261,7 +305,7 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
@NotNull
public VirtualFile[] choose(@Nullable final VirtualFile toSelect, @Nullable final Project project) {
assert !myFileChooserActive: "Current native file chooser should finish before next usage!";
assert !myFileChooserActive : "Current native file chooser should finish before next usage!";
myFileChooserActive = true;
@@ -273,16 +317,18 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
final Window parent = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
if (parent instanceof Frame) {
myFakeDialog = new JDialog((Frame) parent);
} else if (parent instanceof JDialog) {
myFakeDialog = new JDialog((Frame)parent);
}
else if (parent instanceof JDialog) {
myFakeDialog = new JDialog(((JDialog)parent));
} else {
}
else {
myFakeDialog = new JDialog((JFrame)null);
}
myFakeDialog.setModal(true);
myFakeDialog.setUndecorated(true);
myFakeDialog.getRootPane().putClientProperty( "Window.shadow", Boolean.FALSE );
myFakeDialog.getRootPane().putClientProperty("Window.shadow", Boolean.FALSE);
myFakeDialog.setSize(0, 0);
myFakeDialog.setVisible(true);
@@ -290,7 +336,8 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
try {
if (myResultFiles == null) {
return new VirtualFile[0];
} else {
}
else {
return VfsUtil.toVirtualFileArray(myResultFiles);
}
}
@@ -301,12 +348,13 @@ public class MacFileChooserDialogImpl implements MacFileChooserDialog {
}
private static VirtualFile getToSelect(VirtualFile toSelect, Project project) {
final VirtualFile[] selectFile = new VirtualFile[] {null};
final VirtualFile[] selectFile = new VirtualFile[]{null};
if (toSelect == null) {
if (project != null && project.getBaseDir() != null) {
selectFile[0] = project.getBaseDir();
}
} else {
}
else {
selectFile[0] = toSelect.isValid() ? toSelect : null;
}
return selectFile[0];
@@ -50,7 +50,7 @@ public class MacMessagesImpl extends MacMessages{
if (window instanceof JFrame) {
JFrame frame = (JFrame)window;
JRootPane rootPane = frame.getRootPane();
if (rootPane.getClientProperty(MAC_SHEET_ACTIVE) == Boolean.TRUE &&
if (rootPane.getClientProperty(MacUtil.MAC_NATIVE_WINDOW_SHOWING) == Boolean.TRUE &&
fakeDialogTitle.equals(rootPane.getClientProperty(MAC_SHEET_ID))) {
processResult(rootPane, returnCode.intValue(), suppressState.intValue());
break;
@@ -59,7 +59,7 @@ public class MacMessagesImpl extends MacMessages{
else if (window instanceof JDialog) {
JDialog dialog = (JDialog)window;
JRootPane rootPane = dialog.getRootPane();
if (rootPane.getClientProperty(MAC_SHEET_ACTIVE) == Boolean.TRUE &&
if (rootPane.getClientProperty(MacUtil.MAC_NATIVE_WINDOW_SHOWING) == Boolean.TRUE &&
fakeDialogTitle.equals(rootPane.getClientProperty(MAC_SHEET_ID))) {
processResult(rootPane, returnCode.intValue(), suppressState.intValue());
}
@@ -107,10 +107,9 @@ public class MacMessagesImpl extends MacMessages{
rootPane.putClientProperty(MAC_SHEET_RESULT, returnCode);
rootPane.putClientProperty(MAC_SHEET_SUPPRESS, suppressDialog == 1 ? Boolean.TRUE : Boolean.FALSE);
rootPane.putClientProperty(MAC_SHEET_ID, null);
rootPane.putClientProperty(MAC_SHEET_ACTIVE, null);
rootPane.putClientProperty(MacUtil.MAC_NATIVE_WINDOW_SHOWING, null);
}
private static final String MAC_SHEET_ACTIVE = "mac_sheet_active";
private static final String MAC_SHEET_RESULT = "mac_sheet_result";
private static final String MAC_SHEET_SUPPRESS = "mac_sheet_suppress";
private static final String MAC_SHEET_ID = "mac_sheet_id";
@@ -244,10 +243,10 @@ public class MacMessagesImpl extends MacMessages{
}
if (fakeTitle != null) {
pane.putClientProperty(MAC_SHEET_ACTIVE, Boolean.TRUE);
pane.putClientProperty(MacUtil.MAC_NATIVE_WINDOW_SHOWING, Boolean.TRUE);
pane.putClientProperty(MAC_SHEET_ID, fakeTitle);
startModal(pane);
MacUtil.startModal(pane);
Integer result = (Integer)pane.getClientProperty(MAC_SHEET_RESULT);
boolean suppress = Boolean.TRUE == pane.getClientProperty(MAC_SHEET_SUPPRESS);
@@ -330,37 +329,4 @@ public class MacMessagesImpl extends MacMessages{
@Nullable Window window) {
return showAlertDialog(title, okText, alternateText, cancelText, message, window, false, null);
}
private static synchronized void startModal(JRootPane pane) {
try {
if (SwingUtilities.isEventDispatchThread()) {
EventQueue theQueue = pane.getToolkit().getSystemEventQueue();
while (pane.getClientProperty(MAC_SHEET_ACTIVE) == Boolean.TRUE) {
AWTEvent event = theQueue.getNextEvent();
Object source = event.getSource();
if (event instanceof ActiveEvent) {
((ActiveEvent)event).dispatch();
}
else if (source instanceof Component) {
((Component)source).dispatchEvent(event);
}
else if (source instanceof MenuComponent) {
((MenuComponent)source).dispatchEvent(event);
}
else {
System.err.println("Unable to dispatch: " + event);
}
}
}
else {
while (pane.getClientProperty(MAC_SHEET_ACTIVE) == Boolean.TRUE) {
// TODO:
//wait();
}
}
}
catch (InterruptedException ignored) {
}
}
}
@@ -67,12 +67,17 @@ public class SymLinkHandlingTest extends LightPlatformTestCase {
final VirtualFile linkVDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(linkDir);
assertTrue("link=" + linkDir + ", vLink=" + linkVDir, linkVDir != null && linkVDir.isDirectory() && linkVDir.isSymLink());
assertTrue(targetDir.getAbsolutePath(), targetDir.setWritable(true, false) && targetDir.canWrite());
linkVDir.refresh(false, true);
assertTrue(linkVDir.getPath(), linkVDir.isWritable());
assertTrue(targetDir.getAbsolutePath(), targetDir.setWritable(false, false) && !targetDir.canWrite());
linkVDir.refresh(false, true);
assertFalse(linkVDir.getPath(), linkVDir.isWritable());
if (!SystemInfo.isWindows) {
assertTrue(targetDir.getAbsolutePath(), targetDir.setWritable(true, false) && targetDir.canWrite());
linkVDir.refresh(false, true);
assertTrue(linkVDir.getPath(), linkVDir.isWritable());
assertTrue(targetDir.getAbsolutePath(), targetDir.setWritable(false, false) && !targetDir.canWrite());
linkVDir.refresh(false, true);
assertFalse(linkVDir.getPath(), linkVDir.isWritable());
}
else {
assertEquals(linkVDir.getPath(), targetDir.canWrite(), linkVDir.isWritable());
}
}
public void testLinkDeleteIsSafe() throws Exception {
@@ -122,14 +127,20 @@ public class SymLinkHandlingTest extends LightPlatformTestCase {
final File parentDir = linkFile.getParentFile();
assertTrue("link=" + link + ", parent=" + parentDir, parentDir != null && (parentDir.isDirectory() || parentDir.mkdirs()));
final ProcessBuilder builder = new ProcessBuilder("ln", "-s", target, linkFile.getAbsolutePath());
final ProcessBuilder builder;
if (SystemInfo.isWindows) {
builder = new File(target).isDirectory()
? new ProcessBuilder("cmd", "/C", "mklink", "/D", linkFile.getAbsolutePath(), target)
: new ProcessBuilder("cmd", "/C", "mklink", linkFile.getAbsolutePath(), target);
}
else {
builder = new ProcessBuilder("ln", "-s", target, linkFile.getAbsolutePath());
}
final Process process = builder.start();
final int res = process.waitFor();
assertTrue(builder.command() + ": " + res, res == 0);
final File targetFile = new File(target);
assertTrue("target=" + target + ", link=" + linkFile,
linkFile.exists() == targetFile.exists() &&
linkFile.getCanonicalPath().equals(targetFile.getAbsolutePath()) == targetFile.exists());
assertEquals("target=" + target + ", link=" + linkFile, targetFile.exists(), linkFile.exists());
return linkFile;
}
}
@@ -50,12 +50,12 @@ public class SystemInfo {
public static final boolean isMacSystemMenu = isMac && "true".equals(System.getProperty("apple.laf.useScreenMenuBar"));
public static final boolean isFileSystemCaseSensitive = !isWindows && !isOS2 && !isMac;
public static final boolean areSymLinksSupported = isUnix;
public static final boolean areSymLinksSupported = isUnix ||
isWindows && OS_VERSION.compareTo("6.0") >= 0 && isJavaVersionAtLeast("1.7");
public static final boolean is32Bit = ARCH_DATA_MODEL == null || ARCH_DATA_MODEL.equals("32");
public static final boolean is64Bit = !is32Bit;
public static final boolean isAMD64 = "amd64".equals(OS_ARCH);
public static final boolean isMacIntel64 = isMac && "x86_64".equals(OS_ARCH);
public static final String nativeFileManagerName = isMac ? "Finder" : isGnome ? "Nautilus" : isKDE ? "Konqueror" : "Explorer";
@@ -42,8 +42,10 @@ import java.util.regex.Pattern;
@SuppressWarnings({"UtilityClassWithoutPrivateConstructor"})
public class FileUtil {
public static final int MEGABYTE = 1024 * 1024;
public static final String ASYNC_DELETE_EXTENSION = ".__del__";
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.io.FileUtil");
private static final ThreadLocal<byte[]> BUFFER = new ThreadLocal<byte[]>() {
protected byte[] initialValue() {
return new byte[1024 * 20];
@@ -51,9 +53,9 @@ public class FileUtil {
};
// do not use channels to copy files larger than 5 Mb because of possible MapFailed error
private static final long CHANNELS_COPYING_LIMIT = 5L * 1024L * 1024L;
private static final long CHANNELS_COPYING_LIMIT = 5L * MEGABYTE;
private static String ourCanonicalTempPathCache = null;
public static final String ASYNC_DELETE_EXTENSION = ".__del__";
private static final int MAX_FILE_DELETE_ATTEMPTS = 10;
@Nullable
public static String getRelativePath(File base, File file) {
@@ -541,21 +543,22 @@ public class FileUtil {
}
public static boolean delete(@NotNull File file) {
File[] files = file.listFiles();
if (files != null) {
for (File file1 : files) {
if (!delete(file1)) return false;
if (!SymLinkUtil.isSymLink(file)) {
File[] files = file.listFiles();
if (files != null) {
for (File child : files) {
if (!delete(child)) return false;
}
}
}
for (int i = 0; i < 10; i++) {
for (int i = 0; i < MAX_FILE_DELETE_ATTEMPTS; i++) {
if (file.delete() || !file.exists()) return true;
try {
//noinspection BusyWait
Thread.sleep(10);
}
catch (InterruptedException ignored) {
}
catch (InterruptedException ignored) { }
}
return false;
}
@@ -0,0 +1,320 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.util.io;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.ArrayUtil;
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 java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
// todo[r.sh] use NIO2 API after migration to JDK 7
public class SymLinkUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.io.SymLinkUtil");
@Nullable
private static final Mediator ourMediator;
static {
Mediator mediator = null;
if (SystemInfo.areSymLinksSupported) {
if (SystemInfo.isJavaVersionAtLeast("1.7")) {
try {
mediator = new Jdk7MediatorImpl();
mediator.isSymLink("/"); // quick test
}
catch (Throwable t) {
LOG.error(t);
mediator = null;
}
}
if (mediator == null) {
if (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris) {
try {
mediator = new JnaUnixMediatorImpl();
mediator.isSymLink("/"); // quick test
}
catch (Throwable t) {
LOG.error(t);
mediator = null;
}
}
/*else if (SystemInfo.isWindows) {
try {
mediator = new JnaWindowsMediatorImpl();
mediator.isSymLink("/"); // quick test
}
catch (Throwable t) {
LOG.error(t);
mediator = null;
}
}*/
}
}
ourMediator = mediator;
}
private SymLinkUtil() { }
public static boolean isSymLink(@NotNull final File file) {
return isSymLink(file.getAbsolutePath());
}
public static boolean isSymLink(@NotNull final String path) {
try {
return ourMediator != null && ourMediator.isSymLink(path);
}
catch (Exception e) {
LOG.warn(e);
return false;
}
}
@Nullable
public static String resolveSymLink(@NotNull final File file) {
return resolveSymLink(file.getAbsolutePath());
}
@Nullable
public static String resolveSymLink(@NotNull final String path) {
if (ourMediator != null) {
try {
final String realPath = ourMediator.resolveSymLink(path);
if (realPath != null && new File(realPath).exists()) {
return realPath;
}
}
catch (Exception e) {
LOG.warn(e);
}
}
return null;
}
private interface Mediator {
boolean isSymLink(@NotNull final String path) throws Exception;
@Nullable
String resolveSymLink(@NotNull final String path) throws Exception;
}
private static class Jdk7MediatorImpl implements Mediator {
private final Object myDefaultFileSystem;
private final Method myGetPath;
private final Method myIsSymbolicLink;
private final Object myLinkOptions;
private Jdk7MediatorImpl() throws Exception {
myDefaultFileSystem = Class.forName("java.nio.file.FileSystems").getMethod("getDefault").invoke(null);
myGetPath = Class.forName("java.nio.file.FileSystem").getMethod("getPath", String.class, String[].class);
myGetPath.setAccessible(true);
myIsSymbolicLink = Class.forName("java.nio.file.Files").getMethod("isSymbolicLink", Class.forName("java.nio.file.Path"));
myIsSymbolicLink.setAccessible(true);
myLinkOptions = Array.newInstance(Class.forName("java.nio.file.LinkOption"), 0);
}
@Override
public boolean isSymLink(@NotNull final String path) throws Exception {
final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY);
return (Boolean)myIsSymbolicLink.invoke(null, pathObj);
}
@Override
public String resolveSymLink(@NotNull final String path) throws Exception {
final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY);
final Method toRealPath = pathObj.getClass().getMethod("toRealPath", myLinkOptions.getClass());
toRealPath.setAccessible(true);
return toRealPath.invoke(pathObj, myLinkOptions).toString();
}
}
// thanks to SVNKit for the idea
@SuppressWarnings("OctalInteger")
private static class JnaUnixMediatorImpl implements Mediator {
private interface LibC extends Library {
int S_MASK = 0177777;
int S_IFLNK = 0120000;
int lstat(String path, Pointer stat);
int __lxstat64(int ver, String path, Pointer stat);
}
private final LibC myLibC;
private final Memory mySharedMem;
private final int myOffset;
private JnaUnixMediatorImpl() throws Exception {
myLibC = (LibC)Native.loadLibrary("c", LibC.class);
mySharedMem = new Memory(512);
myOffset = SystemInfo.isLinux ? (SystemInfo.is32Bit ? 16 : 24) :
SystemInfo.isMac ? 8 :
SystemInfo.isSolaris ? (SystemInfo.is32Bit ? 20 : 16) :
-1;
if (myOffset < 0) throw new IllegalStateException("Unsupported OS: " + SystemInfo.OS_NAME);
}
@Override
public synchronized boolean isSymLink(@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(myOffset) : mySharedMem.getShort(myOffset)) & LibC.S_MASK;
return (mode & LibC.S_IFLNK) == LibC.S_IFLNK;
}
else {
LOG.debug("lstat(" + path + "): " + res);
return false;
}
}
@Override
public String resolveSymLink(@NotNull final String path) throws Exception {
return new File(path).getCanonicalPath();
}
}
/*private static class JnaWindowsMediatorImpl implements Mediator {
private interface Kernel32 extends StdCallLibrary {
int IO_REPARSE_TAG_SYMLINK = 0xA000000C;
int FILE_ACCESS_FLAGS = 0x0080;
int FILE_SHARE_FLAGS = 0x00000001 | 0x00000002 | 0x00000004;
int OPEN_EXISTING = 3;
int FILE_OPEN_FLAGS = 0x02000000 | 0x00200000;
int FSCTL_GET_REPARSE_POINT = 0x000900A8;
int SYMLINK_FLAG_RELATIVE = 0x00000001;
@SuppressWarnings({"UnusedDeclaration", "MultipleVariablesInDeclaration"})
class Win32FindData extends Structure implements Structure.ByReference {
public int dwFileAttributes;
public int ftCreationTimeL, ftCreationTimeH;
public int ftLastAccessTimeL, ftLastAccessTimeH;
public int ftLastWriteTimeL, ftLastWriteTimeH;
public int lFileSizeH, lFileSizeL;
public int dwReserved0;
public int dwReserved1;
public char[] cFileName = new char[260];
public char[] cAlternateFileName = new char[14];
}
int MAX_SUPPORTED_TARGET_LENGTH = 4 * 1024;
@SuppressWarnings({"UnusedDeclaration", "MultipleVariablesInDeclaration"})
class ReparseDataBuffer extends Structure implements Structure.ByReference {
public NativeLong ReparseTag;
public short ReparseDataLength;
public short Reserved;
public short SubstituteNameOffset, SubstituteNameLength;
public short PrintNameOffset, PrintNameLength;
public NativeLong Flags;
public char[] PathBuffer = new char[MAX_SUPPORTED_TARGET_LENGTH];
}
Pointer INVALID_HANDLE = Pointer.createConstant(-1);
Pointer FindFirstFile(String lpFileName, Win32FindData lpFindFileData);
boolean FindClose(Pointer hFindFile);
Pointer CreateFile(String lpFileName,
int dwDesiredAccess,
int dwShareMode,
@Nullable Pointer lpSecurityAttributes,
int dwCreationDisposition,
int dwFlagsAndAttributes,
@Nullable Pointer hTemplateFile);
boolean CloseHandle(Pointer hObject);
boolean DeviceIoControl(Pointer hDevice,
int dwIoControlCode,
@Nullable Structure.ByReference lpInBuffer,
int nInBufferSize,
@Nullable Structure.ByReference lpOutBuffer,
int nOutBufferSize,
IntByReference lpBytesReturned,
@Nullable Pointer lpOverlapped);
}
private final Kernel32 myKernel32;
private final Kernel32.Win32FindData myFindData;
private final Kernel32.ReparseDataBuffer myReparseData;
private JnaWindowsMediatorImpl() throws Exception {
myKernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class, W32APIOptions.UNICODE_OPTIONS);
myFindData = new Kernel32.Win32FindData();
myReparseData = new Kernel32.ReparseDataBuffer();
}
@SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext")
@Override
public synchronized boolean isSymLink(@NotNull final String path) throws Exception {
synchronized (myFindData) {
myFindData.dwReserved0 = 0;
final Pointer handle = myKernel32.FindFirstFile(path, myFindData);
if (Kernel32.INVALID_HANDLE.equals(handle)) {
LOG.debug("FindFirstFile(" + path + "): " + handle);
return false;
}
myKernel32.FindClose(handle);
return (myFindData.dwReserved0 & Kernel32.IO_REPARSE_TAG_SYMLINK) == Kernel32.IO_REPARSE_TAG_SYMLINK;
}
}
@SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext")
@Override
public String resolveSymLink(@NotNull final String path) throws Exception {
final Pointer handle = myKernel32.CreateFile(path, Kernel32.FILE_ACCESS_FLAGS, Kernel32.FILE_SHARE_FLAGS, null,
Kernel32.OPEN_EXISTING, Kernel32.FILE_OPEN_FLAGS, null);
if (Kernel32.INVALID_HANDLE.equals(handle)) {
LOG.debug("CreateFile(" + path + "): " + handle);
return null;
}
synchronized (myReparseData) {
try {
myReparseData.ReparseTag.setValue(0);
myReparseData.SubstituteNameOffset = myReparseData.SubstituteNameLength = 0;
myReparseData.Flags.setValue(0);
final boolean result = myKernel32.DeviceIoControl(handle, Kernel32.FSCTL_GET_REPARSE_POINT, null, 0,
myReparseData, myReparseData.size(), new IntByReference(), null);
if (!result || myReparseData.ReparseTag.intValue() != Kernel32.IO_REPARSE_TAG_SYMLINK) {
LOG.debug("DeviceIoControl(" + path + "): " + result + "," + myReparseData.ReparseTag);
return null;
}
String target = new String(myReparseData.PathBuffer, myReparseData.SubstituteNameOffset / 2, myReparseData.SubstituteNameLength / 2);
if ((myReparseData.Flags.intValue() & Kernel32.SYMLINK_FLAG_RELATIVE) == Kernel32.SYMLINK_FLAG_RELATIVE) {
return new File(new File(path).getParent(), target).getCanonicalPath();
}
else {
if (target.startsWith("\\??\\") || target.startsWith("\\\\?\\")) {
target = target.substring(4);
}
return new File(target).getCanonicalPath();
}
}
finally {
myKernel32.CloseHandle(handle);
}
}
}
}*/
}
@@ -17,6 +17,9 @@ package com.intellij.ui.mac.foundation;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import static com.intellij.ui.mac.foundation.Foundation.invoke;
import static com.intellij.ui.mac.foundation.Foundation.toStringViaUTF8;
@@ -24,7 +27,8 @@ import static com.intellij.ui.mac.foundation.Foundation.toStringViaUTF8;
* @author pegov
*/
public class MacUtil {
public static final String MAC_NATIVE_WINDOW_SHOWING = "MAC_NATIVE_WINDOW_SHOWING";
private MacUtil() {
}
@@ -63,5 +67,39 @@ public class MacUtil {
return focusedWindow;
}
public static synchronized void startModal(JComponent component) {
try {
if (SwingUtilities.isEventDispatchThread()) {
EventQueue theQueue = component.getToolkit().getSystemEventQueue();
while (component.getClientProperty(MAC_NATIVE_WINDOW_SHOWING) == Boolean.TRUE) {
AWTEvent event = theQueue.getNextEvent();
Object source = event.getSource();
if (event instanceof ActiveEvent) {
((ActiveEvent)event).dispatch();
}
else if (source instanceof Component) {
((Component)source).dispatchEvent(event);
}
else if (source instanceof MenuComponent) {
((MenuComponent)source).dispatchEvent(event);
}
else {
System.err.println("Unable to dispatch: " + event);
}
}
}
else {
assert false: "Should be called from Event-Dispatch Thread only!";
while (component.getClientProperty(MAC_NATIVE_WINDOW_SHOWING) == Boolean.TRUE) {
// TODO:
//wait();
}
}
}
catch (InterruptedException ignored) {
}
}
}
@@ -42,7 +42,12 @@ public class MavenEnvironmentRegistrar implements ApplicationComponent {
private void registerFileTypes() {
// we should not change file types in unit test mode
if (ApplicationManager.getApplication().isUnitTestMode()) return;
FileTypeManager.getInstance().associateExtension(XmlFileType.INSTANCE, MavenConstants.POM_EXTENSION);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
FileTypeManager.getInstance().associateExtension(XmlFileType.INSTANCE, MavenConstants.POM_EXTENSION);
}
});
}
private void registerPathVariable() {
@@ -227,7 +227,12 @@ public class FormReferencesSearcher implements QueryExecutor<PsiReference, Refer
final GlobalSearchScope scope = GlobalSearchScope.projectScope(property.getProject()).intersectWith(globalSearchScope);
final PsiManagerImpl manager = (PsiManagerImpl)property.getManager();
String name = property.getName();
String name = ApplicationManager.getApplication().runReadAction(new Computable<String>() {
@Override
public String compute() {
return property.getName();
}
});
if (name == null) return true;
manager.startBatchFilesProcessingMode();
@@ -178,7 +178,7 @@ instance.method.cannot.override.static.method=Instance method ''{0}'' in ''{1}''
static.method.cannot.override.instance.method=Static method ''{0}'' in ''{1}'' cannot override instance method ''{2}'' in ''{3}''
inconvertible.type.cast=Inconvertible types; cannot cast ''{0}'' to ''{1}''
variable.expected=Variable expected
binary.operator.not.applicable=Operator ''{0}'' cannot be applied to ''{1}'',''{2}''
binary.operator.not.applicable=Operator ''{0}'' cannot be applied to ''{1}'', ''{2}''
unary.operator.not.applicable=Operator ''{0}'' cannot be applied to ''{1}''
return.outside.method=Return outside method
return.from.void.method=Cannot return a value from a method with void result type