diff --git a/bin/linux/fsnotifier b/bin/linux/fsnotifier index e248974f4a94..f7a73126a13e 100755 Binary files a/bin/linux/fsnotifier and b/bin/linux/fsnotifier differ diff --git a/bin/linux/fsnotifier64 b/bin/linux/fsnotifier64 index efba253b08b3..1fc4f32ff7ef 100755 Binary files a/bin/linux/fsnotifier64 and b/bin/linux/fsnotifier64 differ diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspection.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspection.java index d68b2a10970d..7a1b3d99a552 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspection.java @@ -343,11 +343,12 @@ public class UnusedDeclarationInspection extends FilteringInspectionTool { final RefElementImpl refElement = (RefElementImpl)refEntity; if (!refElement.isSuspicious()) return; - final PsiElement element = refElement.getElement(); - if (element == null) return; + PsiFile file = refElement.getContainingFile(); + + if (file == null) return; final boolean isSuppressed = refElement.isSuppressed(getShortName()); - if (!getContext().isToCheckMember(element, UnusedDeclarationInspection.this) || isSuppressed) { - if (isSuppressed || !scope.contains(element)) { + if (!getContext().isToCheckFile(file, UnusedDeclarationInspection.this) || isSuppressed) { + if (isSuppressed || !scope.contains(file)) { getEntryPointsManager().addEntryPoint(refElement, false); } return; @@ -372,6 +373,7 @@ public class UnusedDeclarationInspection extends FilteringInspectionTool { @Override public void visitClass(RefClass aClass) { final PsiClass psiClass = aClass.getElement(); + if (psiClass == null) return; if ( isAddAppletEnabled() && aClass.isApplet() || isAddServletEnabled() && aClass.isServlet()) { diff --git a/java/java-impl/src/com/intellij/testIntegration/TestIntegrationUtils.java b/java/java-impl/src/com/intellij/testIntegration/TestIntegrationUtils.java index ab378ccbc312..ef291f961544 100644 --- a/java/java-impl/src/com/intellij/testIntegration/TestIntegrationUtils.java +++ b/java/java-impl/src/com/intellij/testIntegration/TestIntegrationUtils.java @@ -118,7 +118,7 @@ public class TestIntegrationUtils { public boolean includeMember(PsiMember member) { if (!(member instanceof PsiMethod)) return false; PsiModifierList list = member.getModifierList(); - return list.hasModifierProperty(PsiModifier.PUBLIC); + return !list.hasModifierProperty(PsiModifier.PRIVATE); } }, false); clazz = clazz.getSuperClass(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java index e65932502fce..68ea27d5a7b0 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.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. @@ -30,31 +30,31 @@ import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.PsiSuperMethodImplUtil; import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; import com.intellij.psi.impl.java.stubs.PsiClassStub; -import com.intellij.psi.impl.source.ClassInnerStuffCache; -import com.intellij.psi.impl.source.Constants; -import com.intellij.psi.impl.source.PsiClassImpl; -import com.intellij.psi.impl.source.SourceTreeToPsiMap; +import com.intellij.psi.impl.source.*; import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.SearchScope; +import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; -public class ClsClassImpl extends ClsRepositoryPsiElement> implements PsiClass, PsiQualifiedNamedElement, Queryable { +public class ClsClassImpl extends ClsRepositoryPsiElement> implements PsiExtensibleClass, PsiQualifiedNamedElement, Queryable { + public static final Key DELEGATE_KEY = Key.create("DELEGATE"); + private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClsClassImpl"); - private final ClassInnerStuffCache innersCache = new ClassInnerStuffCache(this); + private final ClassInnerStuffCache myInnersCache = new ClassInnerStuffCache(this); private final PsiIdentifier myNameIdentifier; private final PsiDocComment myDocComment; - public static final Key DELEGATE_KEY = Key.create("DELEGATE"); public ClsClassImpl(final PsiClassStub stub) { super(stub); @@ -70,9 +70,9 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple PsiModifierList modifierList = getModifierList(); PsiReferenceList extendsList = getExtendsList(); PsiReferenceList implementsList = getImplementsList(); - PsiField[] fields = getFields(); - PsiMethod[] methods = getMethods(); - PsiClass[] classes = getInnerClasses(); + List fields = getOwnFields(); + List methods = getOwnMethods(); + List classes = getOwnInnerClasses(); int count = (docComment != null ? 1 : 0) @@ -80,9 +80,9 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple + 1 // name + 1 // extends list + 1 // implementsList - + fields.length - + methods.length - + classes.length; + + fields.size() + + methods.size() + + classes.size(); PsiElement[] children = new PsiElement[count]; int offset = 0; @@ -95,12 +95,12 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple children[offset++] = extendsList; children[offset++] = implementsList; - System.arraycopy(fields, 0, children, offset, fields.length); - offset += fields.length; - System.arraycopy(methods, 0, children, offset, methods.length); - offset += methods.length; - System.arraycopy(classes, 0, children, offset, classes.length); - /*offset += classes.length;*/ + ArrayUtil.copy(fields, children, offset); + offset += fields.size(); + ArrayUtil.copy(methods, children, offset); + offset += methods.size(); + ArrayUtil.copy(classes, children, offset); + /*offset += classes.size();*/ return children; } @@ -166,7 +166,6 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple return getStub().findChildStubByType(JavaStubElementTypes.EXTENDS_LIST).getPsi(); } - @Override @NotNull public PsiReferenceList getImplementsList() { @@ -222,25 +221,43 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple @Override @NotNull public PsiField[] getFields() { - return getStub().getChildrenByType(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY); + return myInnersCache.getFields(); } @Override @NotNull public PsiMethod[] getMethods() { - return getStub().getChildrenByType(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY); + return myInnersCache.getMethods(); } @Override @NotNull public PsiMethod[] getConstructors() { - return PsiImplUtil.getConstructors(this); + return myInnersCache.getConstructors(); } @Override @NotNull public PsiClass[] getInnerClasses() { - return getStub().getChildrenByType(JavaStubElementTypes.CLASS, ARRAY_FACTORY); + return myInnersCache.getInnerClasses(); + } + + @NotNull + @Override + public List getOwnFields() { + return Arrays.asList(getStub().getChildrenByType(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY)); + } + + @NotNull + @Override + public List getOwnMethods() { + return Arrays.asList(getStub().getChildrenByType(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY)); + } + + @NotNull + @Override + public List getOwnInnerClasses() { + return Arrays.asList(getStub().getChildrenByType(JavaStubElementTypes.CLASS, ARRAY_FACTORY)); } @Override @@ -275,7 +292,7 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple @Override public PsiField findFieldByName(String name, boolean checkBases) { - return innersCache.findFieldByName(name, checkBases); + return myInnersCache.findFieldByName(name, checkBases); } @Override @@ -292,7 +309,7 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple @Override @NotNull public PsiMethod[] findMethodsByName(String name, boolean checkBases) { - return innersCache.findMethodsByName(name, checkBases); + return myInnersCache.findMethodsByName(name, checkBases); } @Override @@ -309,7 +326,7 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple @Override public PsiClass findInnerClassByName(String name, boolean checkBases) { - return innersCache.findInnerClassByName(name, checkBases); + return myInnersCache.findInnerClassByName(name, checkBases); } @Override @@ -386,50 +403,50 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple } buffer.append('{'); final int newIndentLevel = indentLevel + getIndentSize(); - PsiField[] fields = getFields(); - if (fields.length > 0) { + List fields = getOwnFields(); + if (fields.size() > 0) { goNextLine(newIndentLevel, buffer); - for (int i = 0; i < fields.length; i++) { - PsiField field = fields[i]; + for (int i = 0; i < fields.size(); i++) { + PsiField field = fields.get(i); ((ClsElementImpl)field).appendMirrorText(newIndentLevel, buffer); if (field instanceof ClsEnumConstantImpl) { - if (i < fields.length - 1 && fields[i + 1] instanceof ClsEnumConstantImpl) { + if (i < fields.size() - 1 && fields.get(i + 1) instanceof ClsEnumConstantImpl) { buffer.append(", "); } else { buffer.append(";"); - if (i < fields.length - 1) { + if (i < fields.size() - 1) { goNextLine(newIndentLevel, buffer); } } - } else if (i < fields.length - 1) { + } else if (i < fields.size() - 1) { goNextLine(newIndentLevel, buffer); } } } - PsiMethod[] methods = getMethods(); - if (methods.length > 0) { + List methods = getOwnMethods(); + if (methods.size() > 0) { goNextLine(newIndentLevel, buffer); goNextLine(newIndentLevel, buffer); - for (int i = 0; i < methods.length; i++) { - PsiMethod method = methods[i]; + for (int i = 0; i < methods.size(); i++) { + PsiMethod method = methods.get(i); ((ClsElementImpl)method).appendMirrorText(newIndentLevel, buffer); - if (i < methods.length - 1) { + if (i < methods.size() - 1) { goNextLine(newIndentLevel, buffer); goNextLine(newIndentLevel, buffer); } } } - PsiClass[] classes = getInnerClasses(); - if (classes.length > 0) { + List classes = getOwnInnerClasses(); + if (classes.size() > 0) { goNextLine(newIndentLevel, buffer); goNextLine(newIndentLevel, buffer); - for (int i = 0; i < classes.length; i++) { - PsiClass aClass = classes[i]; + for (int i = 0; i < classes.size(); i++) { + PsiClass aClass = classes.get(i); ((ClsElementImpl)aClass).appendMirrorText(newIndentLevel, buffer); - if (i < classes.length - 1) { + if (i < classes.size() - 1) { goNextLine(newIndentLevel, buffer); goNextLine(newIndentLevel, buffer); } @@ -459,37 +476,37 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple Ref extLog = Ref.create(true); - PsiField[] fields = getFields(); + List fields = getOwnFields(); PsiField[] mirrorFields = mirror.getFields(); - if (fields.length == mirrorFields.length) { - for (int i = 0; i < fields.length; i++) { - ((ClsElementImpl)fields[i]).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorFields[i])); + if (fields.size() == mirrorFields.length) { + for (int i = 0; i < fields.size(); i++) { + ((ClsElementImpl)fields.get(i)).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorFields[i])); } } else { - log(this, mirror, "fields:" + fields.length + "!=" + mirrorFields.length, extLog); + log(this, mirror, "fields:" + fields.size() + "!=" + mirrorFields.length, extLog); } - PsiMethod[] methods = getMethods(); + List methods = getOwnMethods(); PsiMethod[] mirrorMethods = mirror.getMethods(); - if (methods.length == mirrorMethods.length) { - for (int i = 0; i < methods.length; i++) { - ((ClsElementImpl)methods[i]).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorMethods[i])); + if (methods.size() == mirrorMethods.length) { + for (int i = 0; i < methods.size(); i++) { + ((ClsElementImpl)methods.get(i)).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorMethods[i])); } } else { - log(this, mirror, "methods:" + methods.length + "!=" + mirrorMethods.length, extLog); + log(this, mirror, "methods:" + methods.size() + "!=" + mirrorMethods.length, extLog); } - PsiClass[] classes = getInnerClasses(); + List classes = getOwnInnerClasses(); PsiClass[] mirrorClasses = mirror.getInnerClasses(); - if (classes.length == mirrorClasses.length) { - for (int i = 0; i < classes.length; i++) { - ((ClsElementImpl)classes[i]).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorClasses[i])); + if (classes.size() == mirrorClasses.length) { + for (int i = 0; i < classes.size(); i++) { + ((ClsElementImpl)classes.get(i)).setMirror(SourceTreeToPsiMap.psiToTreeNotNull(mirrorClasses[i])); } } else { - log(this, mirror, "classes:" + classes.length + "!=" + mirrorClasses.length, extLog); + log(this, mirror, "classes:" + classes.size() + "!=" + mirrorClasses.length, extLog); } } @@ -628,5 +645,4 @@ public class ClsClassImpl extends ClsRepositoryPsiElement> imple protected boolean isVisibilitySupported() { return true; } - } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/ClassInnerStuffCache.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/ClassInnerStuffCache.java index 3a0afa1d0b46..d08934a61d44 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/ClassInnerStuffCache.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/ClassInnerStuffCache.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. @@ -37,7 +37,7 @@ import java.util.List; import java.util.Map; public class ClassInnerStuffCache { - private final PsiClass myClass; + private final PsiExtensibleClass myClass; private final MyModificationTracker myTreeChangeTracker; private CachedValue myConstructorsCache; @@ -48,7 +48,7 @@ public class ClassInnerStuffCache { private CachedValue>> myMethodsMapCache; private CachedValue> myInnerClassesMapCache; - public ClassInnerStuffCache(final PsiClass aClass) { + public ClassInnerStuffCache(final PsiExtensibleClass aClass) { myClass = aClass; myTreeChangeTracker = new MyModificationTracker(); } @@ -197,27 +197,21 @@ public class ClassInnerStuffCache { } private PsiField[] getAllFields() { - if (!(myClass instanceof PsiClassImpl)) return myClass.getFields(); - - final PsiField[] own = ((PsiClassImpl)myClass).getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY); + final List own = myClass.getOwnFields(); final List ext = PsiAugmentProvider.collectAugments(myClass, PsiField.class); - return ArrayUtil.mergeArrayAndCollection(own, ext, PsiField.ARRAY_FACTORY); + return ArrayUtil.mergeCollections(own, ext, PsiField.ARRAY_FACTORY); } private PsiMethod[] getAllMethods() { - if (!(myClass instanceof PsiClassImpl)) return myClass.getMethods(); - - final PsiMethod[] own = ((PsiClassImpl)myClass).getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY); + final List own = myClass.getOwnMethods(); final List ext = PsiAugmentProvider.collectAugments(myClass, PsiMethod.class); - return ArrayUtil.mergeArrayAndCollection(own, ext, PsiMethod.ARRAY_FACTORY); + return ArrayUtil.mergeCollections(own, ext, PsiMethod.ARRAY_FACTORY); } private PsiClass[] getAllInnerClasses() { - if (!(myClass instanceof PsiClassImpl)) return myClass.getInnerClasses(); - - final PsiClass[] own = ((PsiClassImpl)myClass).getInnerClassesRaw(); + final List own = myClass.getOwnInnerClasses(); final List ext = PsiAugmentProvider.collectAugments(myClass, PsiClass.class); - return ArrayUtil.mergeArrayAndCollection(own, ext, PsiClass.ARRAY_FACTORY); + return ArrayUtil.mergeCollections(own, ext, PsiClass.ARRAY_FACTORY); } @Nullable diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java index 186b3430cb39..eee618e80bff 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -45,11 +45,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; -public class PsiClassImpl extends JavaStubPsiElement> implements PsiClass, PsiQualifiedNamedElement, Queryable { +public class PsiClassImpl extends JavaStubPsiElement> implements PsiExtensibleClass, PsiQualifiedNamedElement, Queryable { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiClassImpl"); private final ClassInnerStuffCache myInnersCache = new ClassInnerStuffCache(this); @@ -75,7 +76,6 @@ public class PsiClassImpl extends JavaStubPsiElement> implements @Override public void subtreeChanged() { dropCaches(); - super.subtreeChanged(); } @@ -87,9 +87,7 @@ public class PsiClassImpl extends JavaStubPsiElement> implements @Override protected Object clone() { PsiClassImpl clone = (PsiClassImpl)super.clone(); - clone.dropCaches(); - return clone; } @@ -299,8 +297,21 @@ public class PsiClassImpl extends JavaStubPsiElement> implements } @NotNull - public PsiClass[] getInnerClassesRaw() { - return getStubOrPsiChildren(JavaStubElementTypes.CLASS, ARRAY_FACTORY); + @Override + public List getOwnFields() { + return Arrays.asList(getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY)); + } + + @NotNull + @Override + public List getOwnMethods() { + return Arrays.asList(getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY)); + } + + @NotNull + @Override + public List getOwnInnerClasses() { + return Arrays.asList(getStubOrPsiChildren(JavaStubElementTypes.CLASS, ARRAY_FACTORY)); } @Override @@ -577,6 +588,7 @@ public class PsiClassImpl extends JavaStubPsiElement> implements final StubElement parentStub = stub.getParentStub(); final StubBasedPsiElementBase context = (StubBasedPsiElementBase)parentStub.getPsi(); + @SuppressWarnings("unchecked") PsiClass[] classesInScope = (PsiClass[])parentStub.getChildrenByType(Constants.CLASS_BIT_SET, ARRAY_FACTORY); boolean needPreciseContext = false; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiExtensibleClass.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiExtensibleClass.java new file mode 100644 index 000000000000..781e75ced7a5 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiExtensibleClass.java @@ -0,0 +1,34 @@ +/* + * 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.psi.impl.source; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiMethod; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface PsiExtensibleClass extends PsiClass { + @NotNull + List getOwnFields(); + + @NotNull + List getOwnMethods(); + + @NotNull + List getOwnInnerClasses(); +} diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass.log b/java/java-tests/testData/compileServer/incremental/common/addClass.log new file mode 100644 index 000000000000..5aaf6b1395b6 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass.log @@ -0,0 +1,9 @@ +Compiling files: +src/Util.java +End of files +Cleaning output files: +out/production/addClass/Client.class +End of files +Compiling files: +src/Client.java +End of files diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/.name b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/.name new file mode 100644 index 000000000000..d13bc4b0b367 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/.name @@ -0,0 +1 @@ +addClass \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/ant.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/ant.xml new file mode 100644 index 000000000000..2581ca3fe84c --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/ant.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/compiler.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/compiler.xml new file mode 100644 index 000000000000..a1b41c52c724 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/compiler.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/copyright/profiles_settings.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/copyright/profiles_settings.xml new file mode 100644 index 000000000000..3572571ad83c --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/copyright/profiles_settings.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/encodings.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/encodings.xml new file mode 100644 index 000000000000..e206d70d8595 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/misc.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/misc.xml new file mode 100644 index 000000000000..9a249d2930c5 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + http://www.w3.org/1999/xhtml + + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/modules.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/modules.xml new file mode 100644 index 000000000000..ecefc643826b --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/scopes/scope_settings.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/scopes/scope_settings.xml new file mode 100644 index 000000000000..922003b8433b --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/uiDesigner.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/uiDesigner.xml new file mode 100644 index 000000000000..3b0002030884 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/uiDesigner.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/vcs.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/vcs.xml new file mode 100644 index 000000000000..def6a6a18457 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/workspace.xml b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/workspace.xml new file mode 100644 index 000000000000..1ce5e7bc1f83 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/.idea/workspace.xml @@ -0,0 +1,559 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + 5050 + + + + + + + + 1334130093404 + 1334130093404 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No facets are configured + + + + + + + + Gant + + + + + + + + IDEA jdk + + + + + + + + addClass + + + + + + + + IDEA jdk + + + + + + + + + + + + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/Util.java.new b/java/java-tests/testData/compileServer/incremental/common/addClass/Util.java.new new file mode 100644 index 000000000000..1141151cae45 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/Util.java.new @@ -0,0 +1,5 @@ +public class Util { + public static void foo (int x) { + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/addClass.iml b/java/java-tests/testData/compileServer/incremental/common/addClass/addClass.iml new file mode 100644 index 000000000000..0898b5451031 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/addClass.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/common/common.iml b/java/java-tests/testData/compileServer/incremental/common/addClass/common/common.iml new file mode 100644 index 000000000000..d5c074327501 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/common/common.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/common/src/Util.java b/java/java-tests/testData/compileServer/incremental/common/addClass/common/src/Util.java new file mode 100644 index 000000000000..ce35affc1568 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/common/src/Util.java @@ -0,0 +1,12 @@ +/** + * Created with IntelliJ IDEA. + * User: db + * Date: 11.04.12 + * Time: 11:43 + * To change this template use File | Settings | File Templates. + */ +public class Util { + public static void foo () { + + } +} diff --git a/java/java-tests/testData/compileServer/incremental/common/addClass/src/Client.java b/java/java-tests/testData/compileServer/incremental/common/addClass/src/Client.java new file mode 100644 index 000000000000..04b7ad769077 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/addClass/src/Client.java @@ -0,0 +1,12 @@ +/** + * Created with IntelliJ IDEA. + * User: db + * Date: 11.04.12 + * Time: 11:43 + * To change this template use File | Settings | File Templates. + */ +public class Client { + { + Util.foo(); + } +} diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java index 6d9596c3b71c..9a36f6d40ee7 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java @@ -77,6 +77,10 @@ public class CommonTest extends IncrementalTestCase { } public void testDeleteClass4() throws Exception { - doTest(); + doTest(); + } + + public void testAddClass() throws Exception { + doTest(); } } diff --git a/native/fsNotifier/linux/fsnotifier.h b/native/fsNotifier/linux/fsnotifier.h index 0ee04cd12639..f3234e2fde9d 100644 --- a/native/fsNotifier/linux/fsnotifier.h +++ b/native/fsNotifier/linux/fsnotifier.h @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,8 @@ // logging void userlog(int priority, const char* format, ...); +#define CHECK_NULL(p, r) if (p == NULL) { userlog(LOG_ERR, "out of memory"); return r; } + // variable-length array typedef struct __array array; @@ -38,7 +40,7 @@ void array_delete(array* a); void array_delete_vs_data(array* a); -// key/value pairs table +// poor man's hash table typedef struct __table table; table* table_create(int capacity); diff --git a/native/fsNotifier/linux/inotify.c b/native/fsNotifier/linux/inotify.c index ab605424d66c..04d749e48d83 100644 --- a/native/fsNotifier/linux/inotify.c +++ b/native/fsNotifier/linux/inotify.c @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,8 +32,6 @@ #define DEFAULT_SUBDIR_COUNT 5 -#define CHECK_NULL(p) if (p == NULL) { userlog(LOG_ERR, "out of memory"); return ERR_ABORT; } - typedef struct __watch_node { char* name; int wd; @@ -153,9 +151,9 @@ static int add_watch(const char* path, watch_node* parent) { node = malloc(sizeof(watch_node)); - CHECK_NULL(node); + CHECK_NULL(node, ERR_ABORT); node->name = strdup(path); - CHECK_NULL(node->name); + CHECK_NULL(node->name, ERR_ABORT); node->wd = wd; node->parent = parent; node->kids = NULL; @@ -163,9 +161,9 @@ static int add_watch(const char* path, watch_node* parent) { if (parent != NULL) { if (parent->kids == NULL) { parent->kids = array_create(DEFAULT_SUBDIR_COUNT); - CHECK_NULL(parent->kids); + CHECK_NULL(parent->kids, ERR_ABORT); } - CHECK_NULL(array_push(parent->kids, node)); + CHECK_NULL(array_push(parent->kids, node), ERR_ABORT); } if (table_put(watches, wd, node) == NULL) { @@ -238,25 +236,31 @@ static bool is_ignored(const char* path, array* ignores) { return false; } -static int walk_tree(const char* path, watch_node* parent, array* ignores) { +static int walk_tree(const char* path, watch_node* parent, array* ignores, bool recursive) { if (is_ignored(path, ignores)) { return ERR_IGNORE; } - DIR* dir = opendir(path); - if (dir == NULL) { - if (errno == EACCES) { - return ERR_IGNORE; + DIR* dir; + if (recursive) { + dir = opendir(path); + if (dir == NULL) { + if (errno == EACCES) { + return ERR_IGNORE; + } + else if (errno == ENOTDIR) { // "future" root + return add_watch(path, parent); + } + userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno)); + return ERR_CONTINUE; } - else if (errno == ENOTDIR) { // flat root - return add_watch(path, parent); - } - userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno)); - return ERR_CONTINUE; } int id = add_watch(path, parent); - if (id < 0) { + if (!recursive) { + return id; + } + else if (id < 0) { closedir(dir); return id; } @@ -279,7 +283,7 @@ static int walk_tree(const char* path, watch_node* parent, array* ignores) { continue; } - int subdir_id = walk_tree(subdir, table_get(watches, id), ignores); + int subdir_id = walk_tree(subdir, table_get(watches, id), ignores, recursive); if (subdir_id < 0 && subdir_id != ERR_IGNORE) { rm_watch(id, true); id = subdir_id; @@ -293,9 +297,15 @@ static int walk_tree(const char* path, watch_node* parent, array* ignores) { int watch(const char* root, array* ignores) { + bool recursive = true; + if (root[0] == '|') { + root++; + recursive = false; + } + char buf[PATH_MAX]; const char* normalized = realpath(root, buf); - return walk_tree((normalized != NULL ? normalized : root), NULL, ignores); + return walk_tree((normalized != NULL ? normalized : root), NULL, ignores, recursive); } @@ -323,7 +333,7 @@ static bool process_inotify_event(struct inotify_event* event) { } if ((event->mask & IN_CREATE || event->mask & IN_MOVED_TO) && event->mask & IN_ISDIR) { - int result = walk_tree(path, node, NULL); + int result = walk_tree(path, node, NULL, true); if (result < 0 && result != ERR_IGNORE) { return false; } diff --git a/native/fsNotifier/linux/main.c b/native/fsNotifier/linux/main.c index 3bef18521151..81dcdfead702 100644 --- a/native/fsNotifier/linux/main.c +++ b/native/fsNotifier/linux/main.c @@ -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. @@ -33,7 +33,7 @@ #define LOG_ENV_ERROR "error" #define LOG_ENV_OFF "off" -#define VERSION "1.0" +#define VERSION "1.1" #define VERSION_MSG "fsnotifier " VERSION "\n" #define USAGE_MSG \ @@ -58,11 +58,8 @@ typedef struct { static array* roots = NULL; static bool show_warning = true; - static bool self_test = false; -#define CHECK_NULL(p) if (p == NULL) { userlog(LOG_ERR, "out of memory"); return false; } - static void init_log(); static void run_self_test(); static void main_loop(); @@ -223,7 +220,7 @@ static bool read_input() { if (strcmp(line, "ROOTS") == 0) { array* new_roots = array_create(20); - CHECK_NULL(new_roots); + CHECK_NULL(new_roots, false); while (1) { line = read_line(stdin); @@ -235,12 +232,9 @@ static bool read_input() { break; } else { - if (line[0] == '|') line++; // flat roots will be differentiated later - int l = strlen(line); if (l > 1 && line[l-1] == '/') line[l-1] = '\0'; - - CHECK_NULL(array_push(new_roots, strdup(line))); + CHECK_NULL(array_push(new_roots, strdup(line)), false); } } @@ -266,7 +260,7 @@ static bool update_roots(array* new_roots) { } array* unwatchable = array_create(20); - CHECK_NULL(unwatchable); + CHECK_NULL(unwatchable, false); if (!unwatchable_mounts(unwatchable)) { return false; } @@ -312,10 +306,10 @@ static bool register_roots(array* new_roots, array* unwatchable) { } else if (id >= 0) { watch_root* root = malloc(sizeof(watch_root)); - CHECK_NULL(root); + CHECK_NULL(root, false); root->id = id; root->name = new_root; - CHECK_NULL(array_push(roots, root)); + CHECK_NULL(array_push(roots, root), false); } else { if (show_warning && watch_limit_reached()) { @@ -324,7 +318,7 @@ static bool register_roots(array* new_roots, array* unwatchable) { output("MESSAGE\n" INOTIFY_LIMIT_MSG, limit); show_warning = false; // warn only once } - CHECK_NULL(array_push(unwatchable, new_root)); + CHECK_NULL(array_push(unwatchable, new_root), false); } } @@ -362,7 +356,7 @@ static bool unwatchable_mounts(array* mounts) { } if (!is_watchable(dev, point, fs)) { - CHECK_NULL(array_push(mounts, strdup(point))); + CHECK_NULL(array_push(mounts, strdup(point)), false); } } diff --git a/platform/lang-api/src/com/intellij/codeInspection/LocalDummyInspectionTool.java b/platform/lang-api/src/com/intellij/codeInspection/LocalDummyInspectionTool.java index 4420a564c8dd..6a879de0be24 100644 --- a/platform/lang-api/src/com/intellij/codeInspection/LocalDummyInspectionTool.java +++ b/platform/lang-api/src/com/intellij/codeInspection/LocalDummyInspectionTool.java @@ -25,4 +25,6 @@ import org.jetbrains.annotations.NotNull; public abstract class LocalDummyInspectionTool extends LocalInspectionTool { @NotNull public abstract String getProblemGroup(); + + public abstract boolean shouldBeShownInInspectionProfile(); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java index 252b26675a03..b9e0f7d040af 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java @@ -114,7 +114,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx private volatile boolean allowToInterrupt = true; private StatusBarUpdater myStatusBarUpdater; - public DaemonCodeAnalyzerImpl(Project project, DaemonCodeAnalyzerSettings daemonCodeAnalyzerSettings, EditorTracker editorTracker) { + public DaemonCodeAnalyzerImpl(@NotNull Project project, DaemonCodeAnalyzerSettings daemonCodeAnalyzerSettings, EditorTracker editorTracker) { myProject = project; mySettings = daemonCodeAnalyzerSettings; @@ -125,7 +125,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx myPassExecutorService = new PassExecutorService(myProject) { @Override protected void afterApplyInformationToEditor(final TextEditorHighlightingPass pass, - final FileEditor fileEditor, + @NotNull final FileEditor fileEditor, final ProgressIndicator updateProgress) { if (fileEditor instanceof TextEditor) { log(updateProgress, pass, "Apply "); @@ -143,19 +143,20 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx Disposer.register(project, myFileStatusMap); } - static boolean hasErrors(Project project, Document document) { + static boolean hasErrors(@NotNull Project project, @NotNull Document document) { return !processHighlights(document, project, HighlightSeverity.ERROR, 0, document.getTextLength(), CommonProcessors.alwaysFalse()); } @NotNull @TestOnly - public static List getHighlights(Document document, HighlightSeverity minSeverity, Project project) { + public static List getHighlights(@NotNull Document document, HighlightSeverity minSeverity, @NotNull Project project) { List infos = new ArrayList(); processHighlights(document, project, minSeverity, 0, document.getTextLength(), new CommonProcessors.CollectProcessor(infos)); return infos; } + @NotNull public List runMainPasses(@NotNull PsiFile psiFile, @NotNull Document document, @NotNull final ProgressIndicator progress) { @@ -184,6 +185,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx return result; } + @NotNull @TestOnly public List runPasses(@NotNull PsiFile file, @NotNull Document document, @@ -326,7 +328,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx myLastSettings = null; } - void repaintErrorStripeRenderer(Editor editor) { + void repaintErrorStripeRenderer(@NotNull Editor editor) { if (!myProject.isInitialized()) return; final Document document = editor.getDocument(); final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document); @@ -349,7 +351,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx dependencyValidationManager.reloadRules(); } - private static void addScopesToList(final List> scopeList, final NamedScopesHolder holder) { + private static void addScopesToList(@NotNull final List> scopeList, @NotNull final NamedScopesHolder holder) { NamedScope[] scopes = holder.getScopes(); for (NamedScope scope : scopes) { scopeList.add(Pair.create(scope, holder)); @@ -416,7 +418,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx } @Override - public boolean isHighlightingAvailable(PsiFile file) { + public boolean isHighlightingAvailable(@Nullable PsiFile file) { if (file == null || !file.isPhysical()) return false; if (myDisabledHighlightingFiles.contains(PsiUtilCore.getVirtualFile(file))) return false; @@ -453,6 +455,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx stopProcess(true); } + @NotNull public List getPassesToShowProgressFor(Document document) { List allPasses = myPassExecutorService.getAllSubmittedPasses(); List result = new ArrayList(allPasses.size()); @@ -472,7 +475,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx myFileStatusMap.allDirtyScopesAreNull(document); } - public boolean isErrorAnalyzingFinished(PsiFile file) { + public boolean isErrorAnalyzingFinished(@NotNull PsiFile file) { if (myDisposed) return false; Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file); return document != null && @@ -480,6 +483,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx myFileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL) == null; } + @NotNull public FileStatusMap getFileStatusMap() { return myFileStatusMap; } @@ -526,7 +530,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx MarkupModelEx model = (MarkupModelEx)DocumentMarkupModel.forDocument(document, project, true); return model.processRangeHighlightersOverlappingWith(startOffset, endOffset, new Processor() { @Override - public boolean process(RangeHighlighterEx marker) { + public boolean process(@NotNull RangeHighlighterEx marker) { Object tt = marker.getErrorStripeTooltip(); if (!(tt instanceof HighlightInfo)) return true; HighlightInfo info = (HighlightInfo)tt; @@ -549,7 +553,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx MarkupModelEx model = (MarkupModelEx)DocumentMarkupModel.forDocument(document, project, true); return model.processRangeHighlightersOutside(startOffset, endOffset, new Processor() { @Override - public boolean process(RangeHighlighterEx marker) { + public boolean process(@NotNull RangeHighlighterEx marker) { Object tt = marker.getErrorStripeTooltip(); if (!(tt instanceof HighlightInfo)) return true; HighlightInfo info = (HighlightInfo)tt; @@ -569,7 +573,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx @NotNull final Processor processor) { return processHighlights(document, project, null, 0, document.getTextLength(), new Processor() { @Override - public boolean process(HighlightInfo info) { + public boolean process(@NotNull HighlightInfo info) { if (!isOffsetInsideHighlightInfo(offset, info, includeFixRange)) return true; int compare = info.getSeverity().compareTo(minSeverity); @@ -579,11 +583,11 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx } @Nullable - public HighlightInfo findHighlightByOffset(Document document, final int offset, final boolean includeFixRange) { + public HighlightInfo findHighlightByOffset(@NotNull Document document, final int offset, final boolean includeFixRange) { final List foundInfoList = new SmartList(); processHighlightsNearOffset(document, myProject, HighlightSeverity.INFORMATION, offset, includeFixRange, new Processor() { @Override - public boolean process(HighlightInfo info) { + public boolean process(@NotNull HighlightInfo info) { if (!foundInfoList.isEmpty()) { HighlightInfo foundInfo = foundInfoList.get(0); int compare = foundInfo.getSeverity().compareTo(info.getSeverity()); @@ -604,7 +608,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx return new HighlightInfoComposite(foundInfoList); } - private static boolean isOffsetInsideHighlightInfo(int offset, HighlightInfo info, boolean includeFixRange) { + private static boolean isOffsetInsideHighlightInfo(int offset, @NotNull HighlightInfo info, boolean includeFixRange) { RangeHighlighterEx highlighter = info.highlighter; if (highlighter == null || !highlighter.isValid()) return false; int startOffset = highlighter.getStartOffset(); @@ -624,7 +628,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx } @Nullable - public static List getLineMarkers(Document document, Project project) { + public static List getLineMarkers(@NotNull Document document, Project project) { ApplicationManager.getApplication().assertIsDispatchThread(); MarkupModel markup = DocumentMarkupModel.forDocument(document, project, true); return markup.getUserData(MARKERS_IN_EDITOR_DOCUMENT_KEY); @@ -636,7 +640,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx markup.putUserData(MARKERS_IN_EDITOR_DOCUMENT_KEY, lineMarkers); } - public synchronized void setLastIntentionHint(Project project, PsiFile file, Editor editor, ShowIntentionsPass.IntentionsInfo intentions, boolean hasToRecreate) { + public synchronized void setLastIntentionHint(@NotNull Project project, @NotNull PsiFile file, @NotNull Editor editor, @NotNull ShowIntentionsPass.IntentionsInfo intentions, boolean hasToRecreate) { ApplicationManager.getApplication().assertIsDispatchThread(); hideLastIntentionHint(); IntentionHintComponent hintComponent = IntentionHintComponent.showIntentionHint(project, file, editor, intentions, false); @@ -653,12 +657,13 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx } } + @Nullable public synchronized IntentionHintComponent getLastIntentionHint() { return myLastIntentionHint; } @Override - public void writeExternal(Element parentNode) throws WriteExternalException { + public void writeExternal(@NotNull Element parentNode) throws WriteExternalException { Element disableHintsElement = new Element(DISABLE_HINTS_TAG); parentNode.addContent(disableHintsElement); @@ -678,7 +683,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx } @Override - public void readExternal(Element parentNode) throws InvalidDataException { + public void readExternal(@NotNull Element parentNode) throws InvalidDataException { myDisabledHintsFiles.clear(); Element element = parentNode.getChild(DISABLE_HINTS_TAG); @@ -697,6 +702,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx } } + @NotNull private Runnable createUpdateRunnable() { return new Runnable() { @Override @@ -754,6 +760,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx }; } + @NotNull private synchronized DaemonProgressIndicator createUpdateProgress() { DaemonProgressIndicator progress = new DaemonProgressIndicator() { @Override @@ -767,7 +774,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx return progress; } - public boolean canChangeFileSilently(PsiFileSystemItem file) { + public boolean canChangeFileSilently(@NotNull PsiFileSystemItem file) { return myDaemonListeners.canChangeFileSilently(file); } @@ -780,7 +787,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx @NotNull @TestOnly - public static List getFileLevelHighlights(Project project,PsiFile file ) { + public static List getFileLevelHighlights(@NotNull Project project, @NotNull PsiFile file ) { return UpdateHighlightersUtil.getFileLeveleHighlights(project, file); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java index 60d25f3665ed..61bced8f2d27 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java @@ -148,20 +148,20 @@ class DaemonListeners implements Disposable { @Override public void activeEditorsChanged(@NotNull List editors) { List activeEditors = getActiveEditors(); - if (!myActiveEditors.equals(activeEditors)) { - myActiveEditors = activeEditors; - stopDaemon(true); // do not stop daemon if idea loses/gains focus - if (LaterInvocator.isInModalContext()) { - // editor appear in modal context, reenable the daemon - myDaemonCodeAnalyzer.setUpdateByTimerEnabled(true); - } - myDaemonCodeAnalyzer.hideLastIntentionHint(); // mem leak after closing last editor otherwise + if (myActiveEditors.equals(activeEditors)) { + return; + } + myActiveEditors = activeEditors; + stopDaemon(true); // do not stop daemon if idea loses/gains focus + if (LaterInvocator.isInModalContext()) { + // editor appear in modal context, re-enable the daemon + myDaemonCodeAnalyzer.setUpdateByTimerEnabled(true); } } }; myEditorTracker.addEditorTrackerListener(editorTrackerListener, this); - EditorFactoryListener editorFactoryListener = new EditorFactoryAdapter() { + EditorFactoryListener editorFactoryListener = new EditorFactoryListener() { @Override public void editorCreated(@NotNull EditorFactoryEvent event) { Editor editor = event.getEditor(); @@ -175,6 +175,12 @@ class DaemonListeners implements Disposable { } myDaemonCodeAnalyzer.repaintErrorStripeRenderer(editor); } + + @Override + public void editorReleased(@NotNull EditorFactoryEvent event) { + // mem leak after closing last editor otherwise + myDaemonCodeAnalyzer.hideLastIntentionHint(); + } }; EditorFactory.getInstance().addEditorFactoryListener(editorFactoryListener, this); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/Descriptor.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/Descriptor.java index 8f3d92c65875..49de7ada2039 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/Descriptor.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/Descriptor.java @@ -19,6 +19,7 @@ package com.intellij.codeInspection.ex; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.codeInspection.LocalDummyInspectionTool; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.search.scope.packageSet.NamedScope; @@ -43,6 +44,7 @@ public class Descriptor { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.Descriptor"); private final ScopeToolState myState; private final InspectionProfileImpl myInspectionProfile; + private boolean myShouldBeShown = true; public Descriptor(ScopeToolState pair, InspectionProfileImpl inspectionProfile) { myState = pair; @@ -56,6 +58,14 @@ public class Descriptor { myEnabled = ((InspectionProfileImpl)inspectionProfile).isToolEnabled(myKey, pair.getScope()); myTool = tool; myScope = pair.getScope(); + + if (tool instanceof InspectionToolWrapper) { + InspectionProfileEntry inspection = ((InspectionToolWrapper)tool).getTool(); + + if (inspection instanceof LocalDummyInspectionTool) { + myShouldBeShown = ((LocalDummyInspectionTool)inspection).shouldBeShownInInspectionProfile(); + } + } } public boolean equals(Object obj) { @@ -80,6 +90,10 @@ public class Descriptor { myEnabled = enabled; } + public boolean shouldBeShown() { + return myShouldBeShown; + } + public String getText() { return myText; } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index 6f083ee1eff2..61633adec49c 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -71,7 +71,10 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; public class GlobalInspectionContextImpl extends UserDataHolderBase implements GlobalInspectionContext { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.GlobalInspectionContextImpl"); @@ -346,16 +349,15 @@ public class GlobalInspectionContextImpl extends UserDataHolderBase implements G public boolean isToCheckMember(@NotNull RefElement owner, InspectionProfileEntry tool) { - final PsiElement element = owner.getElement(); - return isToCheckMember(element, tool) && !((RefElementImpl)owner).isSuppressed(tool.getShortName()); + return isToCheckFile(((RefElementImpl)owner).getContainingFile(), tool) && !((RefElementImpl)owner).isSuppressed(tool.getShortName()); } - public boolean isToCheckMember(final PsiElement element, final InspectionProfileEntry tool) { + public boolean isToCheckFile(PsiFile file, final InspectionProfileEntry tool) { final Tools tools = myTools.get(tool.getShortName()); if (tools != null) { for (ScopeToolState state : tools.getTools()) { - final NamedScope namedScope = state.getScope(element.getProject()); - if (namedScope == null || namedScope.getValue().contains(element.getContainingFile(), getCurrentProfile().getProfileManager().getScopesManager())) { + final NamedScope namedScope = state.getScope(file.getProject()); + if (namedScope == null || namedScope.getValue().contains(file, getCurrentProfile().getProfileManager().getScopesManager())) { if (state.isEnabled()) { final InspectionProfileEntry entry = state.getTool(); if (entry instanceof InspectionToolWrapper && ((InspectionToolWrapper)entry).getTool() == tool) return true; diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java index b79c4338312d..401c3110e0f1 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java @@ -19,10 +19,7 @@ package com.intellij.codeInspection.ex; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInsight.daemon.InspectionProfileConvertor; -import com.intellij.codeInspection.InspectionProfile; -import com.intellij.codeInspection.InspectionProfileEntry; -import com.intellij.codeInspection.InspectionsBundle; -import com.intellij.codeInspection.ModifiableModel; +import com.intellij.codeInspection.*; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ProjectInspectionToolsConfigurable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ProjectInspectionToolsConfigurable.java index 56d9e859ffed..9d5b5143a75d 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ProjectInspectionToolsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/ProjectInspectionToolsConfigurable.java @@ -58,7 +58,8 @@ public class ProjectInspectionToolsConfigurable extends InspectionToolsConfigura @Override public boolean isModified() { - if (!Comparing.strEqual(getCurrentProfile().getName(), getSelectedObject().getName())) return true; + final InspectionProfileImpl selectedObject = getSelectedObject(); + if (selectedObject != null && !Comparing.strEqual(getCurrentProfile().getName(), selectedObject.getName())) return true; return super.isModified(); } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java index c93c098d9f67..a8d9f80553ab 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java @@ -21,14 +21,8 @@ import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; -import com.intellij.codeInspection.InspectionProfile; -import com.intellij.codeInspection.InspectionProfileEntry; -import com.intellij.codeInspection.InspectionsBundle; -import com.intellij.codeInspection.ModifiableModel; -import com.intellij.codeInspection.ex.Descriptor; -import com.intellij.codeInspection.ex.InspectionProfileImpl; -import com.intellij.codeInspection.ex.InspectionToolRegistrar; -import com.intellij.codeInspection.ex.ScopeToolState; +import com.intellij.codeInspection.*; +import com.intellij.codeInspection.ex.*; import com.intellij.ide.CommonActionsManager; import com.intellij.ide.DefaultTreeExpander; import com.intellij.ide.IdeTooltipManager; @@ -685,7 +679,7 @@ public class SingleInspectionProfilePanel extends JPanel { keySetList.addAll(SearchUtil.findKeys(filter, quated)); } for (Descriptor descriptor : myDescriptors.keySet()) { - if (filter != null && filter.length() > 0 && !isDescriptorAccepted(descriptor, filter, forceInclude, keySetList, quated)) { + if (!descriptor.shouldBeShown() || filter != null && filter.length() > 0 && !isDescriptorAccepted(descriptor, filter, forceInclude, keySetList, quated)) { continue; } final List nonDefaultTools = mySelectedProfile.getNonDefaultTools(descriptor.getKey().toString()); diff --git a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java index b65b5e289ff8..d5cb1d5dd353 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java @@ -173,6 +173,7 @@ public abstract class BaseRefactoringProcessor { } catch (Throwable e) { anyException.set(Boolean.TRUE); + LOG.error(e); } } }; diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java b/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java index e241dad57b2d..69f67380cd9c 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java @@ -203,7 +203,7 @@ public class RenameUtil { }); } - public static void doRenameGenericNamedElement(PsiElement namedElement, String newName, UsageInfo[] usages, + public static void doRenameGenericNamedElement(@NotNull PsiElement namedElement, String newName, UsageInfo[] usages, @Nullable RefactoringElementListener listener) throws IncorrectOperationException { PsiWritableMetaData writableMetaData = null; if (namedElement instanceof PsiMetaOwner) { @@ -213,7 +213,7 @@ public class RenameUtil { } } if (writableMetaData == null && !(namedElement instanceof PsiNamedElement)) { - LOG.error("Unknown element type"); + LOG.error("Unknown element type:" + namedElement); } boolean hasBindables = false; diff --git a/platform/platform-api/src/com/intellij/openapi/editor/event/EditorFactoryAdapter.java b/platform/platform-api/src/com/intellij/openapi/editor/event/EditorFactoryAdapter.java index 79c53b939203..3f3fb311ee8e 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/event/EditorFactoryAdapter.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/event/EditorFactoryAdapter.java @@ -21,9 +21,11 @@ import org.jetbrains.annotations.NotNull; * @author max */ public abstract class EditorFactoryAdapter implements EditorFactoryListener { + @Override public void editorCreated(@NotNull EditorFactoryEvent event) { } + @Override public void editorReleased(@NotNull EditorFactoryEvent event) { } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java index e11872c5a048..62b8a1a7ddf5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/FileWatcher.java @@ -20,34 +20,32 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; +import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.ManagingFS; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.openapi.vfs.watcher.ChangeKind; -import com.intellij.util.PairFunction; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import javax.swing.event.HyperlinkEvent; import java.io.*; import java.util.*; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * @author max */ public class FileWatcher { - @NonNls public static final String PROPERTY_WATCHER_DISABLED = "filewatcher.disabled"; - @NonNls private static final String PROPERTY_WATCHER_EXECUTABLE_PATH = "idea.filewatcher.executable.path"; + @NonNls public static final String PROPERTY_WATCHER_DISABLED = "idea.filewatcher.disabled"; + @NonNls public static final String PROPERTY_WATCHER_EXECUTABLE_PATH = "idea.filewatcher.executable.path"; private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.impl.local.FileWatcher"); @@ -59,15 +57,7 @@ public class FileWatcher { @NonNls private static final String EXIT_COMMAND = "EXIT"; @NonNls private static final String MESSAGE_COMMAND = "MESSAGE"; - private static final PairFunction PATH_COMPARATOR = new PairFunction() { - @Override - public Boolean fun(final String s1, final String s2) { - return SystemInfo.isFileSystemCaseSensitive ? s1.equals(s2) : s1.equalsIgnoreCase(s2); - } - }; - private final Object LOCK = new Object(); - private final Lock SET_ROOTS_LOCK = new ReentrantLock(true); private List myDirtyPaths = new ArrayList(); private List myDirtyRecursivePaths = new ArrayList(); @@ -75,21 +65,23 @@ public class FileWatcher { private List myManualWatchRoots = new ArrayList(); private final List> myMapping = new ArrayList>(); - private List> myCanonicalMapping = new ArrayList>(); - private List myRecursiveWatchRoots = new ArrayList(); private List myFlatWatchRoots = new ArrayList(); + private final Collection myAllPaths = new ArrayList(2); + private final Collection myWatchedPaths = new ArrayList(2); + + private File executable; private volatile Process notifierProcess; private volatile BufferedReader notifierReader; - private volatile BufferedWriter notifierWriter; + private volatile BufferedWriter notifierWriter; private boolean myFailureShownToTheUser = false; private int attemptCount = 0; private static final int MAX_PROCESS_LAUNCH_ATTEMPT_COUNT = 10; private boolean isShuttingDown = false; - private final ManagingFS myManagingFS; + private final ManagingFS myManagingFS; private static final FileWatcher ourInstance = new FileWatcher(); public static FileWatcher getInstance() { @@ -140,7 +132,6 @@ public class FileWatcher { myDirtyRecursivePaths = new ArrayList(); return result; } - } public List getDirtyDirs() { @@ -158,26 +149,16 @@ public class FileWatcher { } public void setWatchRoots(final List recursive, final List flat) { - SET_ROOTS_LOCK.lock(); - try { - synchronized (LOCK) { - if (myRecursiveWatchRoots.equals(recursive) && myFlatWatchRoots.equals(flat)) return; - } - - final List> mapping = new ArrayList>(); - long t = System.nanoTime(); - final List checkedRecursive = checkPaths(recursive, mapping); - final List checkedFlat = checkPaths(flat, mapping); - t = (System.nanoTime() - t) / 1000; - LOG.info((recursive.size() + flat.size()) + " paths checked, " + mapping.size() + " mapped, " + t + " mks"); + synchronized (LOCK) { + if (myRecursiveWatchRoots.equals(recursive) && myFlatWatchRoots.equals(flat)) return; if (isAlive()) { try { writeLine(ROOTS_COMMAND); - for (String path : checkedRecursive) { + for (String path : recursive) { writeLine(path); } - for (String path : checkedFlat) { + for (String path : flat) { writeLine("|" + path); } writeLine("#"); @@ -187,37 +168,10 @@ public class FileWatcher { } } - synchronized (LOCK) { - myRecursiveWatchRoots = recursive; - myFlatWatchRoots = flat; - myMapping.clear(); - myCanonicalMapping = mapping; - } + myRecursiveWatchRoots = recursive; + myFlatWatchRoots = flat; + myMapping.clear(); } - finally { - SET_ROOTS_LOCK.unlock(); - } - } - - private static List checkPaths(final List paths, final List> mapping) { - if (!SystemInfo.areSymLinksSupported) return paths; - - final List checkedPaths = new ArrayList(paths.size()); - for (String path : paths) { - String watched = path; - final String canonical = getCanonicalPath(path); - //noinspection ConstantConditions - if (!PATH_COMPARATOR.fun(path, canonical)) { - mapping.add(Pair.create((watched = canonical), path)); - } - checkedPaths.add(watched); - } - return checkedPaths; - } - - private static String getCanonicalPath(final String path) { - final String realPath = FileSystemUtil.resolveSymLink(path); - return realPath != null ? realPath : path; } private boolean isAlive() { @@ -247,43 +201,34 @@ public class FileWatcher { shutdownProcess(); - String execPath = null; + if (executable == null) { + executable = getExecutable(); - final String altExecPath = System.getProperty(PROPERTY_WATCHER_EXECUTABLE_PATH); - if (altExecPath != null && new File(altExecPath).isFile()) { - execPath = FileUtil.toSystemDependentName(altExecPath); - } - - if (execPath == null) { - final String execName; - execName = getExecutableName(); - if (execName == null) { + if (executable == null) { myFailureShownToTheUser = true; // ignore unsupported platforms return; } - execPath = PathManager.getBinPath() + File.separatorChar + execName; + + if (!executable.exists()) { + notifyOnFailure("File watcher is not found at path: " + executable, null); + return; + } + + if (!executable.canExecute()) { + final String message = "File watcher is not executable: " + executable + ""; + final File exec = executable; + notifyOnFailure(message, new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { + ShowFilePathAction.openFile(exec); + } + }); + return; + } } - final File exec = new File(execPath); - if (!exec.exists()) { - notifyOnFailure("File watcher is not found at path: " + execPath, null); - return; - } - - if (!exec.canExecute()) { - notifyOnFailure("File watcher is not executable: " + execPath +"", new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - ShowFilePathAction.openFile(exec); - } - }); - return; - } - - LOG.info("Starting file watcher: " + execPath); - - notifierProcess = Runtime.getRuntime().exec(new String[]{execPath}); - + LOG.info("Starting file watcher: " + executable); + notifierProcess = Runtime.getRuntime().exec(executable.getAbsolutePath()); notifierReader = new BufferedReader(new InputStreamReader(notifierProcess.getInputStream())); notifierWriter = new BufferedWriter(new OutputStreamWriter(notifierProcess.getOutputStream())); @@ -299,15 +244,43 @@ public class FileWatcher { } @Nullable - private static String getExecutableName() { + private static File getExecutable() { + String execPath = null; + + final String altExecPath = System.getProperty(PROPERTY_WATCHER_EXECUTABLE_PATH); + if (altExecPath != null && new File(altExecPath).isFile()) { + execPath = FileUtil.toSystemDependentName(altExecPath); + } + + if (execPath == null) { + final String execName = getExecutableName(false); + if (execName == null) { + return null; + } + execPath = FileUtil.join(PathManager.getBinPath(), execName); + } + + File exec = new File(execPath); + if (!exec.exists()) { + String homePath = PathManager.getHomePath(); + if (new File(homePath, "community").exists()) { + homePath += File.separator + "community"; + } + exec = new File(FileUtil.join(homePath, "bin", getExecutableName(true))); + } + return exec; + } + + @Nullable + private static String getExecutableName(final boolean withSubDir) { if (SystemInfo.isWindows) { - return "fsnotifier.exe"; + return (withSubDir ? "win" + File.separator : "") + "fsnotifier.exe"; } else if (SystemInfo.isMac) { - return "fsnotifier"; + return (withSubDir ? "mac" + File.separator : "") + "fsnotifier"; } else if (SystemInfo.isLinux) { - return SystemInfo.isAMD64 ? "fsnotifier64" : "fsnotifier"; + return (withSubDir ? "linux" + File.separator : "") + (SystemInfo.isAMD64 ? "fsnotifier64" : "fsnotifier"); } return null; @@ -316,7 +289,8 @@ public class FileWatcher { private void notifyOnFailure(String cause, @Nullable NotificationListener listener) { if (!myFailureShownToTheUser) { myFailureShownToTheUser = true; - Notifications.Bus.notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "External file sync may be slow", cause, NotificationType.WARNING, listener)); + Notifications.Bus.notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "External file sync may be slow", + cause, NotificationType.WARNING, listener)); } } @@ -339,6 +313,32 @@ public class FileWatcher { return notifierProcess != null; } + @TestOnly + public void startup() throws IOException { + final Application app = ApplicationManager.getApplication(); + assert app != null && app.isUnitTestMode() : app; + + myFailureShownToTheUser = true; + attemptCount = 0; + startupProcess(false); + attemptCount = 2 * MAX_PROCESS_LAUNCH_ATTEMPT_COUNT; + if (notifierProcess != null) { + new WatchForChangesThread().start(); + } + } + + @TestOnly + public void shutdown() throws InterruptedException { + final Application app = ApplicationManager.getApplication(); + assert app != null && app.isUnitTestMode() : app; + + final Process process = notifierProcess; + if (process != null) { + shutdownProcess(); + process.waitFor(); + } + } + private class WatchForChangesThread extends Thread { public WatchForChangesThread() { //noinspection HardCodedStringLiteral @@ -396,7 +396,7 @@ public class FileWatcher { final String pathB = readLine(); if (pathB == null || "#".equals(pathB)) break; - pairs.add(new Pair(ensureEndsWithSlash(pathA), ensureEndsWithSlash(pathB))); + pairs.add(Pair.create(preparePathForMapping(pathA), preparePathForMapping(pathB))); } while (true); @@ -414,10 +414,10 @@ public class FileWatcher { } synchronized (LOCK) { - final String watchedPath = checkWatchable(path); - if (watchedPath != null) { + final Collection watchedPaths = checkWatchable(path); + if (!watchedPaths.isEmpty()) { try { - onPathChange(ChangeKind.valueOf(command), watchedPath); + onPathChange(ChangeKind.valueOf(command), watchedPaths); } catch (IllegalArgumentException e) { LOG.error("Illegal watcher command: " + command); @@ -438,9 +438,9 @@ public class FileWatcher { } } - private static String ensureEndsWithSlash(String path) { - if (path.endsWith("/") || path.endsWith(File.separator)) return path; - return path + '/'; + private static String preparePathForMapping(final String path) { + final String localPath = FileUtil.toSystemDependentName(path); + return localPath.endsWith(File.separator) ? localPath : localPath + File.separator; } private void writeLine(String line) throws IOException { @@ -486,64 +486,76 @@ public class FileWatcher { return line; } - public boolean isWatched(VirtualFile file) { - return isOperational() && checkWatchable(file.getPresentableUrl()) != null; + public boolean isWatched(@NotNull final VirtualFile file) { + if (isOperational()) { + synchronized (LOCK) { + return !checkWatchable(file.getPresentableUrl()).isEmpty(); + } + } + return false; } - @Nullable - private String checkWatchable(String path) { - if (path == null) return null; + @NotNull + private Collection checkWatchable(final String reportedPath) { + if (reportedPath == null) return Collections.emptyList(); - for (Pair mapping : myCanonicalMapping) { - if (path.startsWith(mapping.first)) { - path = mapping.second + path.substring(mapping.first.length()); - break; + myAllPaths.clear(); + myAllPaths.add(reportedPath); + for (Pair map : myMapping) { + if (FileUtil.startsWith(reportedPath, map.first)) { + myAllPaths.add(map.second + reportedPath.substring(map.first.length())); + } + else if (FileUtil.startsWith(reportedPath, map.second)) { + myAllPaths.add(map.first + reportedPath.substring(map.second.length())); } } - for (String root : myRecursiveWatchRoots) { - if (FileUtil.startsWith(path, root)) { - return path; + myWatchedPaths.clear(); + ext: + for (String path : myAllPaths) { + for (String root : myRecursiveWatchRoots) { + if (FileUtil.startsWith(path, root)) { + myWatchedPaths.add(path); + continue ext; + } + } + + for (String root : myFlatWatchRoots) { + if (FileUtil.pathsEqual(path, root)) { + myWatchedPaths.add(path); + continue ext; + } + final File parentFile = new File(path).getParentFile(); + if (parentFile != null && FileUtil.pathsEqual(parentFile.getPath(), root)) { + myWatchedPaths.add(path); + continue ext; + } } } - - for (String root : myFlatWatchRoots) { - if (FileUtil.pathsEqual(path, root)) { - return path; - } - final File parentFile = new File(path).getParentFile(); - if (parentFile != null && FileUtil.pathsEqual(parentFile.getPath(), root)) { - return path; - } - } - - return null; + return myWatchedPaths; } - private void onPathChange(final ChangeKind changeKind, final String path) { + private void onPathChange(final ChangeKind changeKind, final Collection paths) { switch (changeKind) { case STATS: case CHANGE: - addPath(path, myDirtyPaths); + myDirtyPaths.addAll(paths); break; case CREATE: case DELETE: - final File parentFile = new File(path).getParentFile(); - if (parentFile != null) { - addPath(parentFile.getPath(), myDirtyPaths); - } - else { - addPath(path, myDirtyPaths); + for (String path : paths) { + final File parent = new File(path).getParentFile(); + myDirtyPaths.add(parent != null ? parent.getPath() : path); } break; case DIRTY: - addPath(path, myDirtyDirs); + myDirtyDirs.addAll(paths); break; case RECDIRTY: - addPath(path, myDirtyRecursivePaths); + myDirtyRecursivePaths.addAll(paths); break; case RESET: @@ -552,19 +564,6 @@ public class FileWatcher { } } - private void addPath(String path, List list) { - list.add(path); - - for (Pair map : myMapping) { - if (FileUtil.startsWith(path, map.getFirst())) { - list.add(map.getSecond() + path.substring(map.getFirst().length())); - } - else if (FileUtil.startsWith(path, map.getSecond())) { - list.add(map.getFirst() + path.substring(map.getSecond().length())); - } - } - } - private void reset() { synchronized (LOCK) { myDirtyPaths.clear(); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java new file mode 100644 index 000000000000..074522040598 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java @@ -0,0 +1,438 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vfs.local; + +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.idea.Bombed; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.impl.local.FileWatcher; +import com.intellij.openapi.vfs.newvfs.BulkFileListener; +import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent; +import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent; +import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent; +import com.intellij.openapi.vfs.newvfs.events.VFileEvent; +import com.intellij.testFramework.PlatformLangTestCase; +import com.intellij.util.Function; +import com.intellij.util.TimeoutUtil; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.messages.MessageBusConnection; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.*; + +public class FileWatcherTest extends PlatformLangTestCase { + private static final int NATIVE_PROCESS_DELAY = 500; // time to event to be caught by native watcher and passed to watcher thread + + private FileWatcher myWatcher; + private LocalFileSystem myFileSystem; + private MessageBusConnection myConnection; + private final List myEvents = new ArrayList(); + + @Override + protected void setUp() throws Exception { + super.setUp(); + + myWatcher = FileWatcher.getInstance(); + assertNotNull(myWatcher); + assertFalse(myWatcher.isOperational()); + myWatcher.startup(); + assertTrue(myWatcher.isOperational()); + + myFileSystem = LocalFileSystem.getInstance(); + assertNotNull(myFileSystem); + + myConnection = ApplicationManager.getApplication().getMessageBus().connect(); + myConnection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() { + @Override + public void before(@NotNull List events) { + } + + @Override + public void after(@NotNull List events) { + synchronized (myEvents) { + myEvents.addAll(events); + myEvents.notifyAll(); + } + } + }); + } + + @Override + protected void tearDown() throws Exception { + try { + myConnection.disconnect(); + myWatcher.shutdown(); + } + finally { + myFileSystem = null; + myWatcher = null; + super.tearDown(); + } + } + + + public void testFileRoot() throws Exception { + final File file = FileUtil.createTempFile("test.", ".txt"); + refresh(file); + final LocalFileSystem.WatchRequest request = watch(file); + try { + FileUtil.writeToFile(file, "new content"); + assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath()); + + FileUtil.delete(file); + assertEvent(VFileDeleteEvent.class, file.getAbsolutePath()); + + FileUtil.writeToFile(file, "re-creation"); + assertEvent(VFileCreateEvent.class, file.getAbsolutePath()); + } + finally { + myFileSystem.removeWatchedRoot(request); + FileUtil.delete(file); + } + } + + public void testNonCanonicallyNamedFileRoot() throws Exception { + if (SystemInfo.isFileSystemCaseSensitive) { + System.out.println("Ignored: case-insensitive FS required"); + return; + } + + final File file = FileUtil.createTempFile("test.", ".txt"); + refresh(file); + + final String watchRoot = file.getAbsolutePath().toUpperCase(Locale.US); + final LocalFileSystem.WatchRequest request = watch(new File(watchRoot)); + try { + FileUtil.writeToFile(file, "new content"); + assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath()); + + FileUtil.delete(file); + assertEvent(VFileDeleteEvent.class, file.getAbsolutePath()); + + FileUtil.writeToFile(file, "re-creation"); + assertEvent(VFileCreateEvent.class, file.getAbsolutePath()); + } + finally { + myFileSystem.removeWatchedRoot(request); + FileUtil.delete(file); + } + } + + public void testDirectoryRecursive() throws Exception { + final File topDir = FileUtil.createTempDirectory("top.", null); + refresh(topDir); + + final LocalFileSystem.WatchRequest request = watch(topDir); + try { + final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null); + assertEvent(VFileCreateEvent.class, subDir.getAbsolutePath()); + refresh(subDir); + + final File file = FileUtil.createTempFile(subDir, "test.", ".txt", true, false); + assertEvent(VFileCreateEvent.class, file.getAbsolutePath()); + + FileUtil.writeToFile(file, "new content"); + assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath()); + + FileUtil.delete(file); + assertEvent(VFileDeleteEvent.class, file.getAbsolutePath()); + + FileUtil.writeToFile(file, "re-creation"); + assertEvent(VFileCreateEvent.class, file.getAbsolutePath()); + } + finally { + myFileSystem.removeWatchedRoot(request); + FileUtil.delete(topDir); + } + } + + public void testDirectoryFlat() throws Exception { + final File topDir = FileUtil.createTempDirectory("top.", null); + final File watchedFile = FileUtil.createTempFile(topDir, "test.", ".txt", true, false); + final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null); + final File unwatchedFile = FileUtil.createTempFile(subDir, "test.", ".txt", true, false); + refresh(topDir); + + final LocalFileSystem.WatchRequest request = watch(topDir, false); + try { + FileUtil.writeToFile(watchedFile, "new content"); + assertEvent(VFileContentChangeEvent.class, watchedFile.getAbsolutePath()); + + FileUtil.writeToFile(unwatchedFile, "new content"); + assertEvent(VFileEvent.class); + } + finally { + myFileSystem.removeWatchedRoot(request); + FileUtil.delete(topDir); + } + } + + public void testDirectoryNonExisting() throws Exception { + final File topDir = FileUtil.createTempDirectory("top.", null); + final File subDir = new File(topDir, "subDir"); + final File file = new File(subDir, "file.txt"); + refresh(topDir); + + final LocalFileSystem.WatchRequest request = watch(subDir); + try { + assertTrue(subDir.toString(), subDir.mkdir()); + assertEvent(VFileCreateEvent.class, subDir.getAbsolutePath()); + refresh(subDir); + + FileUtil.writeToFile(file, "new content"); + assertEvent(VFileCreateEvent.class, file.getAbsolutePath()); + } + finally { + myFileSystem.removeWatchedRoot(request); + FileUtil.delete(topDir); + } + } + + public void testDirectoryOverlapping() throws Exception { + final File topDir = FileUtil.createTempDirectory("top.", null); + final File file1 = FileUtil.createTempFile(topDir, "file1.", ".txt", true, false); + final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null); + final File file2 = FileUtil.createTempFile(subDir, "file2.", ".txt", true, false); + final File sideDir = FileUtil.createTempDirectory("side.", null); + final File file3 = FileUtil.createTempFile(sideDir, "file3.", ".txt", true, false); + refresh(topDir); + refresh(sideDir); + + final LocalFileSystem.WatchRequest request1 = watch(subDir); + final LocalFileSystem.WatchRequest request2 = watch(sideDir); + try { + FileUtil.writeToFile(file1, "new content"); + FileUtil.writeToFile(file2, "new content"); + FileUtil.writeToFile(file3, "new content"); + assertEvent(VFileContentChangeEvent.class, file2.getAbsolutePath(), file3.getAbsolutePath()); + + final LocalFileSystem.WatchRequest request3 = watch(topDir); + try { + FileUtil.writeToFile(file1, "newer content"); + FileUtil.writeToFile(file2, "newer content"); + FileUtil.writeToFile(file3, "newer content"); + assertEvent(VFileContentChangeEvent.class, file1.getAbsolutePath(), file2.getAbsolutePath(), file3.getAbsolutePath()); + } + finally { + unwatch(request3); + } + + FileUtil.writeToFile(file1, "newest content"); + FileUtil.writeToFile(file2, "newest content"); + FileUtil.writeToFile(file3, "newest content"); + assertEvent(VFileContentChangeEvent.class, file2.getAbsolutePath(), file3.getAbsolutePath()); + + FileUtil.delete(file1); + FileUtil.delete(file2); + FileUtil.delete(file3); + assertEvent(VFileDeleteEvent.class, file1.getAbsolutePath(), file2.getAbsolutePath(), file3.getAbsolutePath()); + } + finally { + myFileSystem.removeWatchedRoots(Arrays.asList(request1, request2)); + FileUtil.delete(topDir); + } + } + + @Bombed(user = "roman.shevchenko", year = 2012, month = Calendar.MAY, day = 1) + public void testSymlinkAboveWatchRoot() throws Exception { + final File topDir = FileUtil.createTempDirectory("top.", null); + final File topLink = SymlinkHandlingTest.createTempLink(topDir.getAbsolutePath(), "link"); + final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null); + final File file = FileUtil.createTempFile(subDir, "test.", ".txt", true, false); + final File fileLink = new File(new File(topLink, subDir.getName()), file.getName()); + refresh(topDir); + refresh(topLink); + + final LocalFileSystem.WatchRequest request = watch(topLink); + try { + FileUtil.writeToFile(file, "new content"); + assertEvent(VFileContentChangeEvent.class, fileLink.getAbsolutePath()); + + FileUtil.delete(file); + assertEvent(VFileDeleteEvent.class, fileLink.getAbsolutePath()); + + FileUtil.writeToFile(file, "re-creation"); + assertEvent(VFileCreateEvent.class, fileLink.getAbsolutePath()); + } + finally { + myFileSystem.removeWatchedRoot(request); + FileUtil.delete(topLink); + FileUtil.delete(topDir); + } + } + + @Bombed(user = "roman.shevchenko", year = 2012, month = Calendar.MAY, day = 1) + public void testSymlinkBelowWatchRoot() throws Exception { + final File targetDir = FileUtil.createTempDirectory("top.", null); + final File file = FileUtil.createTempFile(targetDir, "test.", ".txt", true, false); + final File linkDir = FileUtil.createTempDirectory("link.", null); + final File link = new File(linkDir, "link"); + SymlinkHandlingTest.createTempLink(targetDir.getAbsolutePath(), link.getAbsolutePath()); + final File fileLink = new File(link, file.getName()); + refresh(targetDir); + refresh(linkDir); + + final LocalFileSystem.WatchRequest request = watch(linkDir); + try { + FileUtil.writeToFile(file, "new content"); + assertEvent(VFileContentChangeEvent.class, fileLink.getAbsolutePath()); + + FileUtil.delete(file); + assertEvent(VFileDeleteEvent.class, fileLink.getAbsolutePath()); + + FileUtil.writeToFile(file, "re-creation"); + assertEvent(VFileCreateEvent.class, fileLink.getAbsolutePath()); + } + finally { + myFileSystem.removeWatchedRoot(request); + FileUtil.delete(linkDir); + FileUtil.delete(targetDir); + } + } + + public void testSubst() throws Exception { + if (!SystemInfo.isWindows) { + System.out.println("Ignored: Windows required"); + return; + } + + final Set roots = ContainerUtil.map2Set(File.listRoots(), new Function() { + @Override + public Character fun(File root) { + return root.getPath().toLowerCase(Locale.US).charAt(0); + } + }); + char subst = 0; + for (char c = 'e'; c <= 'z'; c++) { + if (!roots.contains(c)) { + subst = c; + break; + } + } + assertFalse("Occupied: " + roots.toString(), subst == 0); + + final File targetDir = FileUtil.createTempDirectory("top.", null); + final File subDir = FileUtil.createTempDirectory(targetDir, "sub.", null); + final File file = FileUtil.createTempFile(subDir, "test.", ".txt", true, false); + final int rv = new GeneralCommandLine("subst", subst + ":", targetDir.getAbsolutePath()).createProcess().waitFor(); + assertEquals(0, rv); + + final File substDir = new File((subst + ":\\").toUpperCase(Locale.US), subDir.getName()); + final File substFile = new File(substDir, file.getName()); + refresh(targetDir); + refresh(substDir); + + final LocalFileSystem.WatchRequest request = watch(substDir); + try { + FileUtil.writeToFile(file, "new content"); + assertEvent(VFileContentChangeEvent.class, substFile.getAbsolutePath()); + + final LocalFileSystem.WatchRequest request2 = watch(targetDir); + try { + FileUtil.delete(file); + assertEvent(VFileDeleteEvent.class, file.getAbsolutePath(), substFile.getAbsolutePath()); + } + finally { + unwatch(request2); + } + + FileUtil.writeToFile(file, "re-creation"); + assertEvent(VFileCreateEvent.class, substFile.getAbsolutePath()); + } + finally { + myFileSystem.removeWatchedRoot(request); + new GeneralCommandLine("subst", subst + ":", "/d").createProcess().waitFor(); + FileUtil.delete(targetDir); + } + } + + + private List getEvents() throws InterruptedException { + TimeoutUtil.sleep(NATIVE_PROCESS_DELAY); + myFileSystem.refresh(false); + + synchronized (myEvents) { + final ArrayList result = new ArrayList(myEvents); + myEvents.clear(); + return result; + } + } + + private void clearEvents() { + myFileSystem.refresh(false); + synchronized (myEvents) { + myEvents.clear(); + } + } + + @NotNull + private LocalFileSystem.WatchRequest watch(final File watchFile) throws InterruptedException { + return watch(watchFile, true); + } + + @NotNull + private LocalFileSystem.WatchRequest watch(final File watchFile, final boolean recursive) throws InterruptedException { + final LocalFileSystem.WatchRequest request = myFileSystem.addRootToWatch(watchFile.getAbsolutePath(), recursive); + assertNotNull(request); + TimeoutUtil.sleep(NATIVE_PROCESS_DELAY); + clearEvents(); + return request; + } + + private void unwatch(final LocalFileSystem.WatchRequest request) throws InterruptedException { + myFileSystem.removeWatchedRoot(request); + TimeoutUtil.sleep(NATIVE_PROCESS_DELAY); + clearEvents(); + } + + private VirtualFile refresh(final File file) { + final VirtualFile vFile = myFileSystem.refreshAndFindFileByIoFile(file); + assertNotNull(file.toString(), vFile); + VfsUtilCore.visitChildrenRecursively(vFile, new VirtualFileVisitor() { + @Override + public boolean visitFile(@NotNull VirtualFile file) { + file.getChildren(); + return true; + } + }); + return vFile; + } + + private void assertEvent(final Class type, final String... paths) throws InterruptedException { + final List events = getEvents(); + assertEquals(events.toString(), paths.length, events.size()); + + final Set pathSet = ContainerUtil.map2Set(paths, new Function() { + @Override + public String fun(final String path) { + return FileUtil.toSystemIndependentName(path); + } + }); + + for (final VFileEvent event : events) { + assertTrue(event.toString(), type.isInstance(event)); + + final VirtualFile eventFile = event.getFile(); + assertNotNull(event.toString(), eventFile); + + assertTrue(eventFile + " not in " + Arrays.toString(paths), pathSet.remove(eventFile.getPath())); + } + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java index 025a888e1a20..2a8cb8d5d453 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java @@ -266,7 +266,7 @@ public class SymlinkHandlingTest extends LightPlatformLangTestCase { } // todo[r.sh] use NIO2 API after migration to JDK 7 - private static File createTempLink(final String target, final String link) throws InterruptedException, ExecutionException { + public static File createTempLink(final String target, final String link) throws InterruptedException, ExecutionException { final boolean isAbsolute = SystemInfo.isUnix && StringUtil.startsWithChar(link, '/') || SystemInfo.isWindows && link.matches("^[c-zC-Z]:[/\\\\].*$"); final File linkFile = isAbsolute ? new File(link) : new File(FileUtil.getTempDirectory(), link); diff --git a/platform/util/src/com/intellij/util/ArrayUtil.java b/platform/util/src/com/intellij/util/ArrayUtil.java index d54b0d612219..081ac6923cd6 100644 --- a/platform/util/src/com/intellij/util/ArrayUtil.java +++ b/platform/util/src/com/intellij/util/ArrayUtil.java @@ -648,13 +648,16 @@ public class ArrayUtil extends ArrayUtilRt { return indexOf(objects, o) >= 0; } + @NotNull public static int[] newIntArray(int count) { return count == 0 ? EMPTY_INT_ARRAY : new int[count]; } + @NotNull public static String[] newStringArray(int count) { return count == 0 ? EMPTY_STRING_ARRAY : new String[count]; } + @NotNull public static Object[] newObjectArray(int count) { return count == 0 ? EMPTY_OBJECT_ARRAY : new Object[count]; @@ -664,7 +667,9 @@ public class ArrayUtil extends ArrayUtilRt { public static E[] ensureExactSize(int count, @NotNull E[] sample) { if (count == sample.length) return sample; - return (E[])Array.newInstance(sample.getClass().getComponentType(), count); + @SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"}) + final E[] array = (E[])Array.newInstance(sample.getClass().getComponentType(), count); + return array; } @Nullable @@ -677,13 +682,15 @@ public class ArrayUtil extends ArrayUtilRt { return array.length > 0 ? array[array.length - 1] : null; } - /** @deprecated use {@linkplain #mergeArrays(Object[], Object[])} (to remove in IDEA 12) */ - public static T[] join(T[] array1, T[] array2) { - return mergeArrays(array1, array2); - } - @NotNull public static String[] toStringArray(@NotNull Collection collection) { return ArrayUtilRt.toStringArray(collection); } + + public static void copy(@NotNull final Collection src, @NotNull final T[] dst, final int dstOffset) { + int i = dstOffset; + for (T t : src) { + dst[i++] = t; + } + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FilePathUnderVcs.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FilePathUnderVcs.java index b5e0e447a739..f3bed0b0a416 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FilePathUnderVcs.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FilePathUnderVcs.java @@ -20,11 +20,12 @@ import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.FilePathImpl; import com.intellij.openapi.vcs.VcsRoot; +import org.jetbrains.annotations.NonNls; public class FilePathUnderVcs { private final FilePath myPath; private final AbstractVcs myVcs; - private int hashcode; + private int hashCode; public FilePathUnderVcs(final FilePath path, final AbstractVcs vcs) { myPath = path; @@ -59,13 +60,15 @@ public class FilePathUnderVcs { @Override public int hashCode() { - if (hashcode == 0) { - hashcode = myPath != null ? myPath.hashCode() : 0; - hashcode = 31 * hashcode + (myVcs != null ? myVcs.getName().hashCode() : 0); + if (hashCode == 0) { + int hc = myPath != null ? myPath.hashCode() : 0; + hc = 31 * hc + (myVcs != null ? myVcs.getName().hashCode() : 0); + hashCode = hc; } - return hashcode; + return hashCode; } + @NonNls @Override public String toString() { return "FilePathUnderVcs{" + diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdatingChangeListBuilder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdatingChangeListBuilder.java index e921957a81e8..d52e8aafbf25 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdatingChangeListBuilder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdatingChangeListBuilder.java @@ -164,6 +164,10 @@ class UpdatingChangeListBuilder implements ChangelistBuilder { checkIfDisposed(); if (isExcluded(file)) return; if (myScope.belongsTo(new FilePathImpl(file))) { + if (ChangeListManagerImpl.DEBUG) { + System.out.println("UpdatingChangeListBuilder.processModifiedWithoutCheckout"); + System.out.println("file = " + file); + } myComposite.getVFHolder(FileHolder.HolderType.MODIFIED_WITHOUT_EDITING).addFile(file); } } diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/AbsoluteLayoutOperation.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/AbsoluteLayoutOperation.java new file mode 100644 index 000000000000..c42fec4c6987 --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/AbsoluteLayoutOperation.java @@ -0,0 +1,46 @@ +/* + * 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.android.designer.designSurface.layout; + +import com.intellij.android.designer.designSurface.AbstractEditOperation; +import com.intellij.android.designer.model.RadViewComponent; +import com.intellij.designer.designSurface.OperationContext; + +/** + * @author Alexander Lobas + */ +public class AbsoluteLayoutOperation extends AbstractEditOperation { + public AbsoluteLayoutOperation(RadViewComponent container, OperationContext context) { + super(container, context); + } + + @Override + public void showFeedback() { + // TODO: Auto-generated method stub + } + + @Override + public void eraseFeedback() { + // TODO: Auto-generated method stub + } + + @Override + public void execute() throws Exception { + if (!myContext.isMove()) { + super.execute(); + } + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/LayoutMarginOperation.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/LayoutMarginOperation.java index 56d34b91ed82..65413cf7b733 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/LayoutMarginOperation.java +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/LayoutMarginOperation.java @@ -43,11 +43,11 @@ import java.util.List; public class LayoutMarginOperation implements EditOperation { public static final String TYPE = "layout_margin"; - private final OperationContext myContext; - private RadViewComponent myComponent; - private RectangleFeedback myFeedback; - private TextFeedback myTextFeedback; - private Rectangle myMargins; + protected final OperationContext myContext; + protected RadViewComponent myComponent; + protected RectangleFeedback myFeedback; + protected TextFeedback myTextFeedback; + protected Rectangle myMargins; public LayoutMarginOperation(OperationContext context) { myContext = context; @@ -71,13 +71,17 @@ public class LayoutMarginOperation implements EditOperation { myTextFeedback.setBorder(new LineMarginBorder(0, 5, 3, 0)); layer.add(myTextFeedback); - myFeedback = new RectangleFeedback(Color.orange, 2); + myFeedback = new RectangleFeedback(getFeedbackColor(), 2); layer.add(myFeedback); layer.repaint(); } } + protected Color getFeedbackColor() { + return Color.orange; + } + @Override public void showFeedback() { createFeedback(); @@ -86,17 +90,20 @@ public class LayoutMarginOperation implements EditOperation { applyMargins(bounds, myMargins); myFeedback.setBounds(bounds); + myTextFeedback.clear(); + fillTextFeedback(); + myTextFeedback.locationTo(myContext.getLocation(), 15); + } + + protected void fillTextFeedback() { Point moveDelta = myContext.getMoveDelta(); Dimension sizeDelta = myContext.getSizeDelta(); int direction = myContext.getResizeDirection(); - myTextFeedback.clear(); - if (direction == Position.WEST) { // left myTextFeedback.append(Integer.toString(myMargins.x - moveDelta.x)); } else if (direction == Position.EAST) { // right - myTextFeedback.append(Integer.toString(myMargins.width + sizeDelta.width)); } else if (direction == Position.NORTH) { // top @@ -107,7 +114,6 @@ public class LayoutMarginOperation implements EditOperation { } myTextFeedback.dimension("dp"); - myTextFeedback.locationTo(myContext.getLocation(), 15); } @Override @@ -181,11 +187,42 @@ public class LayoutMarginOperation implements EditOperation { } } + ////////////////////////////////////////////////////////////////////////////////////////// + // + // ResizePoint + // + ////////////////////////////////////////////////////////////////////////////////////////// + private static final BasicStroke STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, new float[]{1, 2}, 0); public static void points(ResizeSelectionDecorator decorator) { - decorator.addPoint(new ResizePoint() { + pointFeedback(decorator); + decorator.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.WEST, TYPE) { // left + @Override + protected Point getLocation(DecorationLayer layer, RadComponent component) { + Point location = super.getLocation(layer, component); + location.x -= ((RadViewComponent)component).getMargins().x; + return location; + } + }); + + pointRight(decorator, Color.orange, 0.25, TYPE); + + decorator.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.NORTH, TYPE) { // top + @Override + protected Point getLocation(DecorationLayer layer, RadComponent component) { + Point location = super.getLocation(layer, component); + location.y -= ((RadViewComponent)component).getMargins().y; + return location; + } + }); + + pointBottom(decorator, Color.orange, 0.25, TYPE); + } + + protected static void pointFeedback(ResizeSelectionDecorator decorator) { + decorator.addPoint(new ResizePoint() { @Override protected void paint(DecorationLayer layer, Graphics2D g, RadComponent component) { Rectangle bounds = component.getBounds(layer); @@ -216,44 +253,28 @@ public class LayoutMarginOperation implements EditOperation { return null; } }); + } - decorator.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.WEST, LayoutMarginOperation.TYPE) { // left + protected static void pointRight(ResizeSelectionDecorator decorator, Color color, double ySeparator, Object type) { + decorator.addPoint(new DirectionResizePoint(color, Color.black, Position.EAST, type) { @Override protected Point getLocation(DecorationLayer layer, RadComponent component) { Point location = super.getLocation(layer, component); - location.x -= ((RadViewComponent)component).getMargins().x; + location.x += ((RadViewComponent)component).getMargins().width; return location; } - }); + }.move(1, ySeparator)); + } - decorator - .addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.EAST, LayoutMarginOperation.TYPE) { // right - @Override - protected Point getLocation(DecorationLayer layer, RadComponent component) { - Point location = super.getLocation(layer, component); - location.x += ((RadViewComponent)component).getMargins().width; - return location; - } - }.move(1, 0.25)); - - decorator.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.NORTH, LayoutMarginOperation.TYPE) { // top + protected static void pointBottom(ResizeSelectionDecorator decorator, Color color, double xSeparator, Object type) { + decorator.addPoint(new DirectionResizePoint(color, Color.black, Position.SOUTH, type) { @Override protected Point getLocation(DecorationLayer layer, RadComponent component) { Point location = super.getLocation(layer, component); - location.y -= ((RadViewComponent)component).getMargins().y; + location.y += ((RadViewComponent)component).getMargins().height; return location; } - }); - - decorator.addPoint( - new DirectionResizePoint(Color.orange, Color.black, Position.SOUTH, LayoutMarginOperation.TYPE) { // bottom - @Override - protected Point getLocation(DecorationLayer layer, RadComponent component) { - Point location = super.getLocation(layer, component); - location.y += ((RadViewComponent)component).getMargins().height; - return location; - } - }.move(0.25, 1)); + }.move(xSeparator, 1)); } private static void applyMargins(Rectangle bounds, Rectangle margins) { diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/LayoutWeightOperation.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/LayoutWeightOperation.java new file mode 100644 index 000000000000..a9eb2fca3a87 --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/LayoutWeightOperation.java @@ -0,0 +1,122 @@ +/* + * 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.android.designer.designSurface.layout; + +import com.intellij.designer.designSurface.OperationContext; +import com.intellij.designer.designSurface.selection.ResizeSelectionDecorator; +import com.intellij.designer.model.RadComponent; +import com.intellij.designer.utils.Position; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.psi.xml.XmlAttribute; + +import java.awt.*; +import java.text.DecimalFormat; + +/** + * @author Alexander Lobas + */ +public class LayoutWeightOperation extends LayoutMarginOperation { + public static final String TYPE = "layout_weight"; + + private float myWeight; + + public LayoutWeightOperation(OperationContext context) { + super(context); + } + + @Override + public void setComponent(RadComponent component) { + super.setComponent(component); + + try { + myWeight = Float.parseFloat(myComponent.getTag().getAttributeValue("android:layout_weight")); + } + catch (Throwable e) { + myWeight = 0; + } + } + + @Override + protected Color getFeedbackColor() { + return Color.cyan; + } + + @Override + protected void fillTextFeedback() { + Dimension sizeDelta = myContext.getSizeDelta(); + int direction = myContext.getResizeDirection(); + + if (direction == Position.EAST) { // right + myTextFeedback.append(getWeight(sizeDelta.width)); + } + else if (direction == Position.SOUTH) { // bottom + myTextFeedback.append(getWeight(sizeDelta.height)); + } + } + + private static final DecimalFormat FORMAT = new DecimalFormat("#.##"); + + private String getWeight(int value) { + double weight = myWeight + value / 100.0; + if (weight <= 0) { + return "0"; + } + return FORMAT.format(weight); + } + + @Override + public void execute() throws Exception { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + Dimension sizeDelta = myContext.getSizeDelta(); + int direction = myContext.getResizeDirection(); + + if (direction == Position.EAST) { // right + setWeight(sizeDelta.width); + } + else if (direction == Position.SOUTH) { // bottom + setWeight(sizeDelta.height); + } + } + }); + } + + private void setWeight(int value) { + float weight = myWeight + value / 100f; + if (weight <= 0) { + XmlAttribute attribute = myComponent.getTag().getAttribute("android:layout_weight"); + if (attribute != null) { + attribute.delete(); + } + } + else { + myComponent.getTag().setAttribute("android:layout_weight", FORMAT.format(weight).replace(',', '.')); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // ResizePoint + // + ////////////////////////////////////////////////////////////////////////////////////////// + + public static void point(ResizeSelectionDecorator decorator) { + pointFeedback(decorator); + pointRight(decorator, Color.cyan, 0.75, TYPE); + pointBottom(decorator, Color.cyan, 0.75, TYPE); + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/ResizeOperation.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/ResizeOperation.java index d54861641b44..70af4bbc4c40 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/ResizeOperation.java +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/layout/ResizeOperation.java @@ -209,6 +209,9 @@ public class ResizeOperation implements EditOperation { createFeedback(); myBounds = myContext.getTransformedRectangle(myComponent.getBounds(myContext.getArea().getFeedbackLayer())); + myBounds.width = Math.max(myBounds.width, 0); + myBounds.height = Math.max(myBounds.height, 0); + int direction = myContext.getResizeDirection(); if ((direction & Position.EAST) != 0) { diff --git a/plugins/android-designer/src/com/intellij/android/designer/icons/AbsoluteLayout.png b/plugins/android-designer/src/com/intellij/android/designer/icons/AbsoluteLayout.png new file mode 100644 index 000000000000..382b11a207b1 Binary files /dev/null and b/plugins/android-designer/src/com/intellij/android/designer/icons/AbsoluteLayout.png differ diff --git a/plugins/android-designer/src/com/intellij/android/designer/icons/GestureOverlayView.png b/plugins/android-designer/src/com/intellij/android/designer/icons/GestureOverlayView.png new file mode 100644 index 000000000000..911876ed42f5 Binary files /dev/null and b/plugins/android-designer/src/com/intellij/android/designer/icons/GestureOverlayView.png differ diff --git a/plugins/android-designer/src/com/intellij/android/designer/icons/RadioGroup.png b/plugins/android-designer/src/com/intellij/android/designer/icons/RadioGroup.png new file mode 100644 index 000000000000..647d084b8804 Binary files /dev/null and b/plugins/android-designer/src/com/intellij/android/designer/icons/RadioGroup.png differ diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadAbsoluteLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadAbsoluteLayout.java new file mode 100644 index 000000000000..5f8b525c7f7f --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadAbsoluteLayout.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.android.designer.model.layout; + +import com.intellij.android.designer.designSurface.TreeDropToOperation; +import com.intellij.android.designer.designSurface.layout.AbsoluteLayoutOperation; +import com.intellij.android.designer.designSurface.layout.ResizeOperation; +import com.intellij.android.designer.model.RadViewComponent; +import com.intellij.android.designer.model.RadViewLayoutWithData; +import com.intellij.designer.designSurface.ComponentDecorator; +import com.intellij.designer.designSurface.EditOperation; +import com.intellij.designer.designSurface.OperationContext; +import com.intellij.designer.designSurface.selection.ResizeSelectionDecorator; +import com.intellij.designer.model.RadComponent; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; +import java.util.List; + +/** + * @author Alexander Lobas + */ +public class RadAbsoluteLayout extends RadViewLayoutWithData { + private static final String[] LAYOUT_PARAMS = {"AbsoluteLayout_Layout", "ViewGroup_Layout"}; + + private ResizeSelectionDecorator mySelectionDecorator; + + @Override + @NotNull + public String[] getLayoutParams() { + return LAYOUT_PARAMS; + } + + @Override + public EditOperation processChildOperation(OperationContext context) { + if (context.isCreate() || context.isPaste() || context.isAdd() || context.isMove()) { + if (context.isTree()) { + return new TreeDropToOperation(myContainer, context); + } + return new AbsoluteLayoutOperation((RadViewComponent)myContainer, context); + } + if (context.is(ResizeOperation.TYPE)) { + return new ResizeOperation(context); + } + return null; + } + + @Override + public ComponentDecorator getChildSelectionDecorator(RadComponent component, List selection) { + if (mySelectionDecorator == null) { + mySelectionDecorator = new ResizeSelectionDecorator(Color.red, 1); + ResizeOperation.points(mySelectionDecorator); + } + return mySelectionDecorator; + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadLinearLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadLinearLayout.java index 6b3a8a8299f3..12cdc9fd9bb3 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadLinearLayout.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadLinearLayout.java @@ -16,10 +16,7 @@ package com.intellij.android.designer.model.layout; import com.intellij.android.designer.designSurface.TreeDropToOperation; -import com.intellij.android.designer.designSurface.layout.FlowStaticDecorator; -import com.intellij.android.designer.designSurface.layout.LayoutMarginOperation; -import com.intellij.android.designer.designSurface.layout.LinearLayoutOperation; -import com.intellij.android.designer.designSurface.layout.ResizeOperation; +import com.intellij.android.designer.designSurface.layout.*; import com.intellij.android.designer.model.RadViewComponent; import com.intellij.android.designer.model.RadViewLayoutWithData; import com.intellij.designer.actions.AbstractComboBoxAction; @@ -64,7 +61,7 @@ public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDec return LAYOUT_PARAMS; } - private boolean isHorizontal() { + protected boolean isHorizontal() { return !"vertical".equals(((RadViewComponent)myContainer).getTag().getAttributeValue("android:orientation")); } @@ -82,6 +79,9 @@ public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDec else if (context.is(LayoutMarginOperation.TYPE)) { return new LayoutMarginOperation(context); } + else if (context.is(LayoutWeightOperation.TYPE)) { + return new LayoutWeightOperation(context); + } return null; } @@ -115,6 +115,8 @@ public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDec } } + private static final int POINTS_SIZE = 16; + @Override public ComponentDecorator getChildSelectionDecorator(RadComponent component, List selection) { if (mySelectionDecorator == null) { @@ -125,18 +127,31 @@ public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDec boolean horizontal = isHorizontal(); Pair gravity = Gravity.getSides(component); int direction = ((DirectionResizePoint)point).getDirection(); + Rectangle bounds = component.getBounds(); + boolean goodWidth = bounds.width >= POINTS_SIZE; + boolean goodHeight = bounds.height >= POINTS_SIZE; if (direction == Position.WEST) { // left - return horizontal || gravity.first != Gravity.right; + return (horizontal || gravity.first != Gravity.right) && goodHeight; } if (direction == Position.EAST) { // right - return horizontal || gravity.first != Gravity.left; + return (horizontal || gravity.first != Gravity.left) && goodHeight; } if (direction == Position.NORTH) { // top - return !horizontal || gravity.second != Gravity.bottom; + return (!horizontal || gravity.second != Gravity.bottom) && goodWidth; } if (direction == Position.SOUTH) { // bottom - return !horizontal || gravity.second != Gravity.top; + return (!horizontal || gravity.second != Gravity.top) && goodWidth; + } + } + if (point.getType() == LayoutWeightOperation.TYPE) { + int direction = ((DirectionResizePoint)point).getDirection(); + + if (direction == Position.EAST) { // right + return isHorizontal() && component.getBounds().height >= POINTS_SIZE; + } + if (direction == Position.SOUTH) { // bottom + return !isHorizontal() && component.getBounds().width >= POINTS_SIZE; } } return true; @@ -147,6 +162,7 @@ public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDec mySelectionDecorator.clear(); if (selection.size() == 1) { LayoutMarginOperation.points(mySelectionDecorator); + LayoutWeightOperation.point(mySelectionDecorator); } ResizeOperation.points(mySelectionDecorator); diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadRadioGroupLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadRadioGroupLayout.java new file mode 100644 index 000000000000..93689059da6f --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/model/layout/RadRadioGroupLayout.java @@ -0,0 +1,28 @@ +/* + * 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.android.designer.model.layout; + +import com.intellij.android.designer.model.RadViewComponent; + +/** + * @author Alexander Lobas + */ +public class RadRadioGroupLayout extends RadLinearLayout { + @Override + protected boolean isHorizontal() { + return "horizontal".equals(((RadViewComponent)myContainer).getTag().getAttributeValue("android:orientation")); + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml b/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml index a61e17996723..8f9b927d0e53 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml +++ b/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml @@ -545,6 +545,26 @@ + + + + + + + + + + ]]> + + + + @@ -983,6 +1003,28 @@ + + + + + + + + + + + + ]]> + + + + + + + + + + + ]]> + + + @@ -1063,6 +1123,7 @@ + @@ -1073,6 +1134,7 @@ + diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/renderers/ResourceRenderer.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/renderers/ResourceRenderer.java index 4288b7a7da4b..79b1365d0568 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/renderers/ResourceRenderer.java +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/renderers/ResourceRenderer.java @@ -60,6 +60,7 @@ public class ResourceRenderer implements PropertyRenderer { String value = (String)object; if (myBooleanRenderer != null && (StringUtil.isEmpty(value) || "false".equals(value) || "true".equals(value))) { + myBooleanRenderer.setText(value); return myBooleanRenderer.getComponent(component, "true".equals(value), selected, hasFocus); } diff --git a/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java b/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java index d6db275a63c7..f52243482aa3 100644 --- a/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java +++ b/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java @@ -52,7 +52,9 @@ public class AndroidCommonUtils { private static final Pattern WARNING_PATTERN = Pattern.compile(".*warning.*"); private static final Pattern ERROR_PATTERN = Pattern.compile(".*error.*"); private static final Pattern EXCEPTION_PATTERN = Pattern.compile(".*exception.*"); + private static Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class"); + private static Pattern MANIFEST_PATTERN = Pattern.compile("Manifest(\\$.*)?\\.class"); public static final Pattern COMPILER_MESSAGE_PATTERN = Pattern.compile("(.+):(\\d+):.+"); @@ -204,7 +206,7 @@ public class AndroidCommonUtils { private static void addFileToJar(@NotNull File file, @NotNull File rootDirectory, - boolean packRClasses, + boolean packRAndManifestClasses, @NotNull List> files) throws IOException { @@ -213,7 +215,7 @@ public class AndroidCommonUtils { if (children != null) { for (File child : children) { - addFileToJar(child, rootDirectory, packRClasses, files); + addFileToJar(child, rootDirectory, packRAndManifestClasses, files); } } } @@ -222,7 +224,9 @@ public class AndroidCommonUtils { return; } - if (!packRClasses && R_PATTERN.matcher(file.getName()).matches()) { + if (!packRAndManifestClasses && + (R_PATTERN.matcher(file.getName()).matches() || + MANIFEST_PATTERN.matcher(file.getName()).matches())) { return; } diff --git a/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java b/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java index a0c9497b5c38..021fd3e43888 100644 --- a/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java +++ b/plugins/android/src/org/jetbrains/android/AndroidResourcesLineMarkerProvider.java @@ -24,6 +24,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; @@ -174,8 +175,9 @@ public class AndroidResourcesLineMarkerProvider implements LineMarkerProvider { private static Map> buildLocalResourceMap(@NotNull Project project, @NotNull final LocalResourceManager resManager) { final Map> result = new HashMap>(); - Collection resourceFiles = resManager.getResourceElements(); - for (Resources res : resourceFiles) { + List> resourceFiles = resManager.getResourceElements(); + for (Pair pair : resourceFiles) { + final Resources res = pair.getFirst(); for (ResourceType valueResourceType : AndroidResourceUtil.VALUE_RESOURCE_TYPES) { for (ResourceElement valueResource : AndroidResourceUtil.getValueResourcesFromElement(valueResourceType.getName(), res)) { addResource(valueResourceType.getName(), valueResource, result); diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index fec52eded011..8a469cede0f7 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -729,8 +729,9 @@ public class AndroidCompileUtil { } } - for (final Resources resources : manager.getResourceElements()) { - waitForSmartMode(project); + for (final Pair pair : manager.getResourceElements()) { + final Resources resources = pair.getFirst(); + waitForSmartMode(project); ApplicationManager.getApplication().runReadAction(new Runnable() { @Override diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java index 3e71190f831e..577d46b4d98b 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidPrecompileTask.java @@ -19,10 +19,7 @@ import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.CompilerConfigurationImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.compiler.CompilationStatusAdapter; -import com.intellij.openapi.compiler.CompileContext; -import com.intellij.openapi.compiler.CompileTask; -import com.intellij.openapi.compiler.CompilerManager; +import com.intellij.openapi.compiler.*; import com.intellij.openapi.compiler.options.ExcludeEntryDescription; import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration; import com.intellij.openapi.diagnostic.Logger; @@ -31,7 +28,10 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.DependencyScope; +import com.intellij.openapi.roots.ModuleOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; @@ -39,6 +39,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.hash.HashSet; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidRootUtil; +import org.jetbrains.android.maven.AndroidMavenUtil; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.annotations.NotNull; @@ -57,6 +58,8 @@ public class AndroidPrecompileTask implements CompileTask { @Override public boolean execute(CompileContext context) { + checkAndroidDependencies(context); + final Project project = context.getProject(); ExcludedEntriesConfiguration configuration = @@ -109,6 +112,42 @@ public class AndroidPrecompileTask implements CompileTask { } return true; } + + private static void checkAndroidDependencies(@NotNull CompileContext context) { + for (Module module : context.getCompileScope().getAffectedModules()) { + final AndroidFacet facet = AndroidFacet.getInstance(module); + + if (facet != null && !facet.getConfiguration().LIBRARY_PROJECT) { + + for (OrderEntry entry : ModuleRootManager.getInstance(module).getOrderEntries()) { + if (entry instanceof ModuleOrderEntry) { + final ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)entry; + + if (moduleOrderEntry.getScope() == DependencyScope.COMPILE) { + final Module depModule = moduleOrderEntry.getModule(); + + if (depModule != null) { + final AndroidFacet depFacet = AndroidFacet.getInstance(depModule); + + if (depFacet != null && !depFacet.getConfiguration().LIBRARY_PROJECT) { + String message = "Suspicious module dependency " + + module.getName() + + " -> " + + depModule.getName() + + ": Android application module depends on other application module. Possibly, you should "; + if (AndroidMavenUtil.isMavenizedModule(depModule)) { + message += "change packaging type of module " + depModule.getName() + " to 'apklib' in pom.xml file or "; + } + message += "change dependency scope to 'Provided'."; + context.addMessage(CompilerMessageCategory.WARNING, message, null, -1, -1); + } + } + } + } + } + } + } + } private static void clearResCache(@NotNull AndroidFacet facet, @NotNull CompileContext context) { final Module module = facet.getModule(); diff --git a/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java b/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java index 6947ffd6e52b..0b1e8f17b196 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java +++ b/plugins/android/src/org/jetbrains/android/compiler/ResourceNamesValidityState.java @@ -1,29 +1,44 @@ package org.jetbrains.android.compiler; +import com.android.resources.ResourceType; import com.android.sdklib.IAndroidTarget; import com.intellij.openapi.compiler.ValidityState; import com.intellij.openapi.module.Module; +import com.intellij.openapi.roots.ContentIterator; +import com.intellij.openapi.roots.impl.FileIndexImplUtil; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.containers.HashSet; +import com.intellij.openapi.vfs.VirtualFileFilter; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.HashMap; +import org.jetbrains.android.dom.resources.ResourceElement; +import org.jetbrains.android.dom.resources.Resources; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidRootUtil; +import org.jetbrains.android.resourceManagers.LocalResourceManager; +import org.jetbrains.android.resourceManagers.ResourceManager; import org.jetbrains.android.sdk.AndroidPlatform; +import org.jetbrains.android.util.AndroidResourceUtil; import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.util.Set; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; /** * @author Eugene.Kudelevsky */ public class ResourceNamesValidityState implements ValidityState { - private final Set myResourcesSet = new HashSet(); + private final Map myResources = new HashMap(); + private final String myAndroidTargetHashString; private final long myManifestTimestamp; - + public ResourceNamesValidityState(@NotNull Module module) { final AndroidFacet facet = AndroidFacet.getInstance(module); assert facet != null; @@ -35,19 +50,83 @@ public class ResourceNamesValidityState implements ValidityState { final VirtualFile manifestFile = AndroidRootUtil.getManifestFile(facet); myManifestTimestamp = manifestFile != null ? manifestFile.getModificationStamp() : -1; - AndroidCompileUtil.collectAllResources(facet, myResourcesSet); + final LocalResourceManager manager = facet.getLocalResourceManager(); + + for (final Pair pair : manager.getResourceElements()) { + final Resources resources = pair.getFirst(); + final VirtualFile file = pair.getSecond(); + + for (final ResourceType resType : AndroidResourceUtil.VALUE_RESOURCE_TYPES) { + addValueResources(file, resType, AndroidResourceUtil.getValueResourcesFromElement(resType.getName(), resources), myResources); + } + addValueResources(file, ResourceType.ATTR, resources.getAttrs(), myResources); + addValueResources(file, ResourceType.DECLARE_STYLEABLE, resources.getDeclareStyleables(), myResources); + } + + for (final VirtualFile subdir : manager.getResourceSubdirs(null)) { + final String subdirName = subdir.getName(); + final int index = subdirName.indexOf('-'); + final String typeName = index >= 0 ? subdirName.substring(0, index) : subdirName; + final ResourceType type = ResourceType.getEnum(typeName); + final boolean idProvidingResource = type != null && ArrayUtil.find(ResourceManager.ID_PROVIDING_RESOURCE_TYPES, type) >= 0; + + FileIndexImplUtil.iterateRecursively(subdir, VirtualFileFilter.ALL, new ContentIterator() { + @Override + public boolean processFile(VirtualFile fileOrDir) { + if (!fileOrDir.isDirectory()) { + MyResourceFileData data = myResources.get(fileOrDir.getPath()); + if (data == null) { + data = new MyResourceFileData(); + myResources.put(fileOrDir.getPath(), data); + } + + if (idProvidingResource) { + data.setTimestamp(fileOrDir.getTimeStamp()); + } + } + return true; + } + }); + } } - + + private static void addValueResources(VirtualFile file, + ResourceType resType, + Collection resourceElements, + Map result) { + for (ResourceElement element : resourceElements) { + final String name = element.getName().getValue(); + + if (name != null) { + MyResourceFileData data = result.get(file.getPath()); + if (data == null) { + data = new MyResourceFileData(); + result.put(file.getPath(), data); + } + data.addValueResource(new ResourceEntry(resType.getName(), name)); + } + } + } + public ResourceNamesValidityState(@NotNull DataInput in) throws IOException { myAndroidTargetHashString = in.readUTF(); myManifestTimestamp = in.readLong(); - + final int resourcesCount = in.readInt(); - + for (int i = 0; i < resourcesCount; i++) { - final String type = in.readUTF(); - final String name = in.readUTF(); - myResourcesSet.add(new ResourceEntry(type, name)); + final String filePath = in.readUTF(); + + final int valueResourcesCount = in.readInt(); + final List valueResources = new ArrayList(valueResourcesCount); + + for (int j = 0; j < valueResourcesCount; j++) { + final String resType = in.readUTF(); + final String resName = in.readUTF(); + valueResources.add(new ResourceEntry(resType, resName)); + } + final long fileTimestamp = in.readLong(); + myResources.put(filePath, new MyResourceFileData(valueResources, fileTimestamp)); } } @@ -56,24 +135,86 @@ public class ResourceNamesValidityState implements ValidityState { if (!(otherState instanceof ResourceNamesValidityState)) { return false; } - + final ResourceNamesValidityState other = (ResourceNamesValidityState)otherState; return other.myAndroidTargetHashString.equals(myAndroidTargetHashString) && other.myManifestTimestamp == myManifestTimestamp && - other.myResourcesSet.equals(myResourcesSet); + other.myResources.equals(myResources); } @Override public void save(DataOutput out) throws IOException { out.writeUTF(myAndroidTargetHashString); out.writeLong(myManifestTimestamp); - - out.writeInt(myResourcesSet.size()); - - for (ResourceEntry resourceEntry : myResourcesSet) { - out.writeUTF(resourceEntry.getType()); - out.writeUTF(resourceEntry.getName()); + + out.writeInt(myResources.size()); + + for (Map.Entry entry : myResources.entrySet()) { + out.writeUTF(entry.getKey()); + + final MyResourceFileData data = entry.getValue(); + final List valueResources = data.getValueResources(); + out.writeInt(valueResources.size()); + + for (ResourceEntry resource : valueResources) { + out.writeUTF(resource.getType()); + out.writeUTF(resource.getName()); + } + out.writeLong(data.getTimestamp()); + } + } + + private static class MyResourceFileData { + // order matters because of id assigning in R.java + private final List myValueResources; + + private long myTimestamp; + + MyResourceFileData() { + this(new ArrayList(), 0); + } + + private MyResourceFileData(@NotNull List valueResources, long timestamp) { + myValueResources = valueResources; + myTimestamp = timestamp; + } + + @NotNull + List getValueResources() { + return myValueResources; + } + + long getTimestamp() { + return myTimestamp; + } + + public void setTimestamp(long timestamp) { + myTimestamp = timestamp; + } + + public void addValueResource(@NotNull ResourceEntry entry) { + myValueResources.add(entry); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MyResourceFileData data = (MyResourceFileData)o; + + if (myTimestamp != data.myTimestamp) return false; + if (!myValueResources.equals(data.myValueResources)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myValueResources.hashCode(); + result = 31 * result + (int)(myTimestamp ^ (myTimestamp >>> 32)); + return result; } } } diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java index 8ee3f3974bf7..16cd938f0d43 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/LocalResourceManager.java @@ -24,6 +24,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -96,7 +97,7 @@ public class LocalResourceManager extends ResourceManager { return AndroidRootUtil.getResourceDir(getFacet()); } - public List getResourceElements() { + public List> getResourceElements() { return getResourceElements(null); } @@ -200,7 +201,8 @@ public class LocalResourceManager extends ResourceManager { @NotNull public List findAttrs(@NotNull String name) { List list = new ArrayList(); - for (Resources res : getResourceElements()) { + for (Pair pair : getResourceElements()) { + final Resources res = pair.getFirst(); for (Attr attr : res.getAttrs()) { if (name.equals(attr.getName().getValue())) { list.add(attr); @@ -219,7 +221,8 @@ public class LocalResourceManager extends ResourceManager { public List findStyleables(@NotNull String name) { List list = new ArrayList(); - for (Resources res : getResourceElements()) { + for (Pair pair : getResourceElements()) { + final Resources res = pair.getFirst(); for (DeclareStyleable styleable : res.getDeclareStyleables()) { if (name.equals(styleable.getName().getValue())) { list.add(styleable); diff --git a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java index 0220bfeed1c7..dbdd4a6b5d9f 100644 --- a/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java +++ b/plugins/android/src/org/jetbrains/android/resourceManagers/ResourceManager.java @@ -17,11 +17,11 @@ package org.jetbrains.android.resourceManagers; import com.android.resources.ResourceType; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; @@ -53,7 +53,9 @@ import static java.util.Collections.addAll; * @author coyote */ public abstract class ResourceManager { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.resourceManagers.LocalResourceManager"); + public static final ResourceType[] ID_PROVIDING_RESOURCE_TYPES = new ResourceType[] { + ResourceType.LAYOUT, ResourceType.MENU + }; protected final Module myModule; protected final AndroidFacet myFacet; @@ -130,17 +132,19 @@ public abstract class ResourceManager { return findResourceFiles(resType, null, true); } - protected List getResourceElements(@Nullable Set files) { + protected List> getResourceElements(@Nullable Set files) { return getRootDomElements(Resources.class, files); } - private List getRootDomElements(@NotNull Class elementType, - @Nullable Set files) { - final List result = new ArrayList(); + private List> getRootDomElements(@NotNull Class elementType, + @Nullable Set files) { + final List> result = new ArrayList>(); for (VirtualFile file : getAllValueResourceFiles()) { if ((files == null || files.contains(file)) && file.isValid()) { - T element = AndroidUtils.loadDomElement(myModule, file, elementType); - if (element != null) result.add(element); + final T element = AndroidUtils.loadDomElement(myModule, file, elementType); + if (element != null) { + result.add(new Pair(element, file)); + } } } return result; @@ -162,8 +166,9 @@ public abstract class ResourceManager { protected List getValueResources(@NotNull final String resourceType, @Nullable Set files) { final List result = new ArrayList(); - Collection resourceFiles = getResourceElements(files); - for (final Resources resources : resourceFiles) { + List> resourceFiles = getResourceElements(files); + for (final Pair pair : resourceFiles) { + final Resources resources = pair.getFirst(); ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { @@ -337,10 +342,11 @@ public abstract class ResourceManager { } @NotNull - private List getResourceSubdirsToSearchIds() { + public List getResourceSubdirsToSearchIds() { final List resSubdirs = new ArrayList(); - resSubdirs.addAll(getResourceSubdirs(ResourceType.LAYOUT.getName())); - resSubdirs.addAll(getResourceSubdirs(ResourceType.MENU.getName())); + for (ResourceType type : ID_PROVIDING_RESOURCE_TYPES) { + resSubdirs.addAll(getResourceSubdirs(type.getName())); + } return resSubdirs; } diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchUiUtil.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchUiUtil.java index cbb17dfc7a05..9f5341545ec4 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchUiUtil.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchUiUtil.java @@ -77,7 +77,7 @@ public class GitBranchUiUtil { */ @Nullable public static String getNewBranchNameFromUser(@NotNull Project project, @NotNull Collection repositories, @NotNull String dialogTitle) { - return Messages.showInputDialog(project, "Enter the name of new branch", dialogTitle, Messages.getQuestionIcon(), "", + return Messages.showInputDialog(project, "Enter the name of new branch:", dialogTitle, Messages.getQuestionIcon(), "", GitNewBranchNameValidator.newInstance(repositories)); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyTargetElementEvaluator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyTargetElementEvaluator.java index 01500a678208..a81a55bb942f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyTargetElementEvaluator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/GroovyTargetElementEvaluator.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.codeInsight; import com.intellij.codeInsight.TargetElementEvaluator; +import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; @@ -34,6 +35,9 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrRenameableLightEle * @author Maxim.Medvedev */ public class GroovyTargetElementEvaluator implements TargetElementEvaluator { + + public static final Key NAVIGATION_ELEMENT_IS_NOT_TARGET = Key.create("GroovyTargetElementEvaluator.DONT_FOLLOW_NAVIGATION_ELEMENT"); + public boolean includeSelfInGotoImplementation(@NotNull PsiElement element) { return false; } @@ -79,7 +83,8 @@ public class GroovyTargetElementEvaluator implements TargetElementEvaluator { @Nullable public static PsiElement correctSearchTargets(@Nullable PsiElement target) { - if (target != null && !(target instanceof GrAccessorMethod) && !target.isPhysical()) { + if (target != null && !(target instanceof GrAccessorMethod) && !target.isPhysical() + && target.getUserData(NAVIGATION_ELEMENT_IS_NOT_TARGET) == null) { return target.getNavigationElement(); } return target; diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenRepositoriesConfigurable.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenRepositoriesConfigurable.java index 5db81b975933..01bed087383f 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenRepositoriesConfigurable.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenRepositoriesConfigurable.java @@ -95,8 +95,10 @@ public class MavenRepositoriesConfigurable extends BaseConfigurable implements S final String value = (String)myServiceList.getSelectedValue(); final String text = Messages.showInputDialog("Artifactory or Nexus Service URL", "Add Service URL", Messages.getQuestionIcon(), value == null ? "http://" : value, new URLInputVaslidator()); - myModel.add(text); - myServiceList.setSelectedValue(text, true); + if (StringUtil.isNotEmpty(text)) { + myModel.add(text); + myServiceList.setSelectedValue(text, true); + } } }); myEditButton.addActionListener(new ActionListener() { @@ -105,7 +107,7 @@ public class MavenRepositoriesConfigurable extends BaseConfigurable implements S final int index = myServiceList.getSelectedIndex(); final String text = Messages.showInputDialog("Artifactory or Nexus Service URL", "Edit Service URL", Messages.getQuestionIcon(), myModel.getElementAt(index), new URLInputVaslidator()); - if (text != null) { + if (StringUtil.isNotEmpty(text)) { myModel.setElementAt(text, index); } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenArtifactUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenArtifactUtil.java index a5870b0b9b9d..cba0ef493d4e 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenArtifactUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenArtifactUtil.java @@ -125,20 +125,24 @@ public class MavenArtifactUtil { if (!file.exists()) return null; ZipFile jar = new ZipFile(file); - ZipEntry entry = jar.getEntry(MAVEN_PLUGIN_DESCRIPTOR); - - if (entry == null) { - MavenLog.LOG.info(IndicesBundle.message("repository.plugin.corrupt", file)); - return null; - } - - InputStream is = jar.getInputStream(entry); try { - byte[] bytes = FileUtil.loadBytes(is); - return new MavenPluginInfo(bytes); + ZipEntry entry = jar.getEntry(MAVEN_PLUGIN_DESCRIPTOR); + + if (entry == null) { + MavenLog.LOG.info(IndicesBundle.message("repository.plugin.corrupt", file)); + return null; + } + + InputStream is = jar.getInputStream(entry); + try { + byte[] bytes = FileUtil.loadBytes(is); + return new MavenPluginInfo(bytes); + } + finally { + is.close(); + } } finally { - is.close(); jar.close(); } } diff --git a/plugins/properties/src/com/intellij/codeInspection/duplicatePropertyInspection/DuplicatePropertyInspection.java b/plugins/properties/src/com/intellij/codeInspection/duplicatePropertyInspection/DuplicatePropertyInspection.java index e46ee8da92c3..085a8a9f5e25 100644 --- a/plugins/properties/src/com/intellij/codeInspection/duplicatePropertyInspection/DuplicatePropertyInspection.java +++ b/plugins/properties/src/com/intellij/codeInspection/duplicatePropertyInspection/DuplicatePropertyInspection.java @@ -145,7 +145,7 @@ public class DuplicatePropertyInspection extends GlobalSimpleInspectionTool { private void checkFile(final PsiFile file, final InspectionManager manager, GlobalInspectionContextImpl context, final RefManager refManager, final ProblemDescriptionsProcessor processor) { if (!(file instanceof PropertiesFile)) return; - if (!context.isToCheckMember(file, this)) return; + if (!context.isToCheckFile(file, this)) return; final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(file.getProject()); final PropertiesFile propertiesFile = (PropertiesFile)file; final List properties = propertiesFile.getProperties(); diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/english.dic b/plugins/spellchecker/src/com/intellij/spellchecker/english.dic index 788f32996ce3..b6d95a792ff3 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/english.dic +++ b/plugins/spellchecker/src/com/intellij/spellchecker/english.dic @@ -133140,6 +133140,7 @@ unwarrantably unwarranted unwary unwashed +unwatch unwatchable unwatched unwavering diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java index 0ad958e47c7c..3fc28e1d7f66 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlTagInsertHandler.java @@ -31,6 +31,7 @@ import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; import com.intellij.codeInspection.htmlInspections.RequiredAttributesInspection; import com.intellij.lang.ASTNode; import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.command.undo.UndoManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; @@ -170,6 +171,12 @@ public class XmlTagInsertHandler implements InsertHandler { if (myAttrValueMarker == null) { return; } + + final UndoManager manager = UndoManager.getInstance(project); + if (manager.isUndoInProgress() || manager.isRedoInProgress()) { + return; + } + if (chooseAttributeName) { final int startOffset = myAttrValueMarker.getStartOffset(); final int endOffset = myAttrValueMarker.getEndOffset();