Merge remote branch 'origin/master'

This commit is contained in:
irengrig
2012-07-23 17:50:37 +04:00
121 changed files with 4499 additions and 2928 deletions
@@ -15,6 +15,8 @@
*/
package com.intellij.compiler;
import com.intellij.compiler.make.CacheCorruptedException;
import com.intellij.compiler.make.CachingSearcher;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.extensions.ExtensionPointName;
@@ -25,5 +27,5 @@ import com.intellij.openapi.extensions.ExtensionPointName;
public interface DependencyProcessor {
ExtensionPointName<DependencyProcessor> EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.compiler.makeDependencyProcessor");
void processDependencies(CompileContext context, int classQualifiedName);
void processDependencies(CompileContext context, int classQualifiedName, CachingSearcher searcher) throws CacheCorruptedException;
}
@@ -429,7 +429,7 @@ public class DependencyCache {
}
changedRetentionPolicyDependencyProcessor.checkAnnotationRetentionPolicyChanges(qName);
for (DependencyProcessor additionalProcessor : DependencyProcessor.EXTENSION_POINT_NAME.getExtensions()) {
additionalProcessor.processDependencies(context, qName);
additionalProcessor.processDependencies(context, qName, searcher);
}
}
else {
@@ -33,6 +33,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Collection;
public class BreakpointChooser {
@@ -149,7 +150,7 @@ public class BreakpointChooser {
};
setSelectedBreakpoint(baseBreakpoint);
myActionToolbar = ActionManager.getInstance().createActionToolbar("asdad", new DefaultActionGroup(myComboBoxAction), true);
myActionToolbar.setLayoutPolicy(ActionToolbar.WRAP_LAYOUT_POLICY);
}
public void setBreakpointItems(Collection<BreakpointItem> items) {
@@ -570,7 +570,7 @@ public abstract class BreakpointPropertiesPanel {
PsiElement context = breakpoint.getEvaluationElement();
myPassCountCheckbox.setSelected(breakpoint.COUNT_FILTER_ENABLED);
myConditionCheckbox.setSelected(breakpoint.CONDITION_ENABLED);
myConditionCheckbox.setSelected(breakpoint.CONDITION_ENABLED || breakpoint.getCondition() == null || breakpoint.getCondition().isEmpty());
myConditionCombo.setEnabled(myBreakpoint.CONDITION_ENABLED);
@@ -18,6 +18,7 @@ package com.intellij.ide.actions;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.highlighter.ProjectFileType;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
@@ -76,6 +77,6 @@ public class OpenProjectAction extends AnAction implements DumbAware {
@Override
public void update(AnActionEvent e) {
super.update(e);
e.getPresentation().setVisible(false);
e.getPresentation().setVisible(ActionPlaces.WELCOME_SCREEN.equals(e.getPlace()));
}
}
@@ -163,7 +163,6 @@ public class ModulesConfigurator implements ModulesProvider, ModuleEditor.Change
}
};
myModuleEditors.add(moduleEditor);
myModuleEditors.add(moduleEditor);
moduleEditor.addChangeListener(this);
@@ -154,7 +154,7 @@ class JavaClassNameInsertHandler implements InsertHandler<JavaPsiClassReferenceE
final PsiModifierListOwner parent = PsiTreeUtil.getParentOfType(elementAt, PsiModifierListOwner.class, false, PsiCodeBlock.class);
if (parent != null) {
for (PsiMethod m : ((PsiClass)item.getObject()).getMethods()) {
if (!(m instanceof PsiAnnotationMethod)) continue;
if (!PsiUtil.isAnnotationMethod(m)) continue;
final PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)m).getDefaultValue();
if (defaultValue == null) return true;
}
@@ -145,10 +145,19 @@ public class JavaCompletionContributor extends CompletionContributor {
}
if (psiElement().inside(PsiAnnotationParameterList.class).accepts(position)) {
return new OrFilter(new ClassFilter(PsiAnnotationMethod.class),
ElementClassFilter.CLASS,
ElementClassFilter.PACKAGE_FILTER,
new AndFilter(new ClassFilter(PsiField.class), new ModifierFilter(PsiModifier.STATIC, PsiModifier.FINAL)));
OrFilter orFilter = new OrFilter(ElementClassFilter.CLASS,
ElementClassFilter.PACKAGE_FILTER,
new AndFilter(new ClassFilter(PsiField.class),
new ModifierFilter(PsiModifier.STATIC, PsiModifier.FINAL)));
if (psiElement().insideStarting(psiNameValuePair()).accepts(position)) {
orFilter.addFilter(new ClassFilter(PsiAnnotationMethod.class) {
@Override
public boolean isAcceptable(Object element, PsiElement context) {
return element instanceof PsiAnnotationMethod && PsiUtil.isAnnotationMethod((PsiElement)element);
}
});
}
return orFilter;
}
if (psiElement().afterLeaf("=").inside(PsiVariable.class).accepts(position)) {
@@ -135,6 +135,7 @@ public class JavaCompletionData extends JavaAwareCompletionData {
and(JavaSmartCompletionContributor.INSIDE_EXPRESSION,
not(psiElement().afterLeaf(PsiKeyword.CASE)),
not(psiElement().afterLeaf(psiElement().withText(".").afterLeaf(PsiKeyword.THIS, PsiKeyword.SUPER))),
not(psiElement().inside(PsiAnnotation.class)),
not(START_SWITCH));
public static final AndFilter CLASS_START = new AndFilter(
@@ -142,7 +143,8 @@ public class JavaCompletionData extends JavaAwareCompletionData {
END_OF_BLOCK,
new PatternFilter(psiElement().afterLeaf(
or(
psiElement().withoutText(".").inside(psiElement(PsiModifierList.class).withParent(not(psiElement(PsiParameter.class)))),
psiElement().withoutText(".").inside(psiElement(PsiModifierList.class).withParent(not(psiElement(PsiParameter.class)))).andNot(
psiElement().inside(PsiAnnotationParameterList.class)),
psiElement().isNull())))
),
new PatternFilter(not(psiElement().afterLeaf("@"))));
@@ -486,8 +488,10 @@ public class JavaCompletionData extends JavaAwareCompletionData {
}
if (EXPR_KEYWORDS.accepts(position)) {
result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE));
result.addElement(createKeyword(position, PsiKeyword.NULL));
if (PsiTreeUtil.getParentOfType(position, PsiAnnotation.class) == null) {
result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.NEW), TailType.INSERT_SPACE));
result.addElement(createKeyword(position, PsiKeyword.NULL));
}
result.addElement(createKeyword(position, PsiKeyword.TRUE));
result.addElement(createKeyword(position, PsiKeyword.FALSE));
}
@@ -25,6 +25,7 @@ import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -150,7 +151,7 @@ public class PreferByKindWeigher extends LookupElementWeigher {
return MyResult.classLiteral;
}
if (object instanceof PsiAnnotationMethod && ((PsiAnnotationMethod)object).getContainingClass().isAnnotationType()) {
if (object instanceof PsiMethod && PsiUtil.isAnnotationMethod((PsiElement)object)) {
return MyResult.annoMethod;
}
@@ -188,7 +188,7 @@ public class AnnotationsHighlightUtil {
PsiMethod[] annotationMethods = aClass.getMethods();
List<String> missed = new ArrayList<String>();
for (PsiMethod method : annotationMethods) {
if (method instanceof PsiAnnotationMethod) {
if (PsiUtil.isAnnotationMethod(method)) {
PsiAnnotationMethod annotationMethod = (PsiAnnotationMethod)method;
if (annotationMethod.getDefaultValue() == null) {
if (!names.contains(annotationMethod.getName())) {
@@ -216,7 +216,7 @@ public class AnnotationsHighlightUtil {
@Nullable
public static HighlightInfo checkConstantExpression(PsiExpression expression) {
final PsiElement parent = expression.getParent();
if (parent instanceof PsiAnnotationMethod || parent instanceof PsiNameValuePair || parent instanceof PsiArrayInitializerMemberValue) {
if (PsiUtil.isAnnotationMethod(parent) || parent instanceof PsiNameValuePair || parent instanceof PsiArrayInitializerMemberValue) {
if (!PsiUtil.isConstantExpression(expression)) {
return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, JavaErrorMessages.message("annotation.nonconstant.attribute.value"));
}
@@ -312,7 +312,7 @@ public class AnnotationsHighlightUtil {
}
public static HighlightInfo checkAnnotationDeclaration(final PsiElement parent, final PsiReferenceList list) {
if (parent instanceof PsiAnnotationMethod) {
if (PsiUtil.isAnnotationMethod(parent)) {
PsiAnnotationMethod method = (PsiAnnotationMethod)parent;
if (list == method.getThrowsList()) {
return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, list, JavaErrorMessages.message("annotation.members.may.not.have.throws.list"));
@@ -925,7 +925,7 @@ public class GenericsHighlightUtil {
parameterList,
JavaErrorMessages.message("generics.enum.may.not.have.type.parameters"));
}
if (parent instanceof PsiAnnotationMethod) {
if (PsiUtil.isAnnotationMethod(parent)) {
return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parameterList, JavaErrorMessages.message("generics.annotation.members.may.not.have.type.parameters"));
}
else if (parent instanceof PsiClass && ((PsiClass)parent).isAnnotationType()) {
@@ -2537,7 +2537,7 @@ public class HighlightUtil {
@Nullable
static HighlightInfo checkAnnotationMethodParameters(@NotNull PsiParameterList list) {
if (list.getParent() instanceof PsiAnnotationMethod && list.getParametersCount() > 0) {
if (PsiUtil.isAnnotationMethod(list.getParent()) && list.getParametersCount() > 0) {
final String message = JavaErrorMessages.message("annotation.interface.members.may.not.have.parameters");
return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, list, message);
}
@@ -190,7 +190,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (parent instanceof PsiNameValuePair) {
method = (PsiMethod)parent.getReference().resolve();
}
else if (parent instanceof PsiAnnotationMethod) {
else if (PsiUtil.isAnnotationMethod(parent)) {
method = (PsiMethod)parent;
}
if (method != null) {
File diff suppressed because it is too large Load Diff
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.lang.parameterInfo.*;
import com.intellij.openapi.project.DumbAware;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -122,6 +123,6 @@ public class AnnotationParameterInfoHandler implements ParameterInfoHandler<PsiA
if (pair == null) return null;
final PsiReference reference = pair.getReference();
final PsiElement resolved = reference != null ? reference.resolve():null;
return resolved instanceof PsiAnnotationMethod ? (PsiAnnotationMethod)resolved : null;
return PsiUtil.isAnnotationMethod(resolved) ? (PsiAnnotationMethod)resolved : null;
}
}
@@ -78,7 +78,12 @@ public class AddSingleMemberStaticImportAction extends PsiElementBaseIntentionAc
}
}
}
if (importList.findSingleImportStatement(refExpr.getReferenceName()) == null) {
final PsiImportStatementBase importStatement = importList.findSingleImportStatement(refExpr.getReferenceName());
if (importStatement == null) {
return qName;
}
final PsiElement resolve = importStatement.resolve();
if (resolve instanceof PsiMember && ((PsiMember)resolve).getContainingClass() != aClass) {
return qName;
}
}
@@ -7,6 +7,7 @@ import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.UsageSearchContext;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
@@ -37,7 +38,7 @@ public class MethodUsagesSearcher extends QueryExecutorBase<PsiReference, Method
processConstructorReferences(consumer, method, searchScope, !strictSignatureSearch, strictSignatureSearch, collector);
}
if (method instanceof PsiAnnotationMethod &&
if (PsiUtil.isAnnotationMethod(method) &&
PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(method.getName()) &&
method.getParameterList().getParametersCount() == 0) {
ReferencesSearch.search(method.getContainingClass(), p.getScope()).forEach(PsiAnnotationMethodReferencesSearcher.createImplicitDefaultAnnotationMethodConsumer(
@@ -2,6 +2,7 @@ package com.intellij.psi.impl.search;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Processor;
import com.intellij.util.Query;
import com.intellij.util.QueryExecutor;
@@ -15,7 +16,7 @@ public class PsiAnnotationMethodReferencesSearcher implements QueryExecutor<PsiR
@Override
public boolean execute(@NotNull final ReferencesSearch.SearchParameters p, @NotNull final Processor<PsiReference> consumer) {
final PsiElement refElement = p.getElementToSearch();
if (refElement instanceof PsiAnnotationMethod) {
if (PsiUtil.isAnnotationMethod(refElement)) {
PsiMethod method = (PsiMethod)refElement;
if (PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(method.getName()) && method.getParameterList().getParametersCount() == 0) {
final Query<PsiReference> query = ReferencesSearch.search(method.getContainingClass(), p.getScope(), p.isIgnoreAccessScope());
@@ -15,6 +15,7 @@
*/
package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
@@ -29,6 +30,8 @@ import java.util.List;
* Date: 7/17/12
*/
public class LambdaUtil {
private static final Logger LOG = Logger.getInstance("#" + LambdaUtil.class.getName());
@Nullable
public static String checkInterfaceFunctional(@NotNull PsiClass psiClass) {
final List<MethodSignature> signatures = findFunctionCandidates(psiClass);
@@ -120,7 +123,14 @@ public class LambdaUtil {
if (methodSignature != null) {
final PsiType[] types = methodSignature.getParameterTypes();
if (parameterIndex < types.length) {
return resolveResult.getSubstitutor().substitute(types[parameterIndex]);
final PsiType psiType = resolveResult.getSubstitutor().substitute(types[parameterIndex]);
if (psiType instanceof PsiWildcardType) {
final PsiType bound = ((PsiWildcardType)psiType).getBound();
if (bound != null) {
return bound;
}
}
return psiType;
}
}
}
@@ -140,6 +150,10 @@ public class LambdaUtil {
else if (parent instanceof PsiVariable) {
type = ((PsiVariable)parent).getType();
}
else if (parent instanceof PsiAssignmentExpression) {
final PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression();
type = lExpression.getType();
}
else if (parent instanceof PsiExpressionList) {
final PsiExpressionList expressionList = (PsiExpressionList)parent;
final int lambdaIdx = ArrayUtil.find(expressionList.getExpressions(), lambdaExpression);
@@ -173,7 +187,8 @@ public class LambdaUtil {
public static boolean isAcceptable(PsiLambdaExpression lambdaExpression, final PsiType leftType) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(leftType);
final MethodSignature methodSignature = getFunction(resolveResult.getElement());
final PsiClass psiClass = resolveResult.getElement();
final MethodSignature methodSignature = getFunction(psiClass);
if (methodSignature == null) return false;
final PsiParameter[] lambdaParameters = lambdaExpression.getParameterList().getParameters();
final PsiType[] parameterTypes = methodSignature.getParameterTypes();
@@ -182,11 +197,57 @@ public class LambdaUtil {
PsiParameter parameter = lambdaParameters[lambdaParamIdx];
final PsiTypeElement typeElement = parameter.getTypeElement();
if (typeElement != null) {
if (!typeElement.getType().equals(resolveResult.getSubstitutor().substitute(parameterTypes[lambdaParamIdx]))) {
final PsiType lambdaFormalType = typeElement.getType();
final PsiType methodParameterType = parameterTypes[lambdaParamIdx];
if (lambdaFormalType instanceof PsiPrimitiveType){
if (methodParameterType instanceof PsiPrimitiveType) return methodParameterType.isAssignableFrom(lambdaFormalType);
return false;
}
if (!resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodParameterType)).isAssignableFrom(lambdaFormalType)) {
return false;
}
}
}
PsiMethod method = null;
LOG.assertTrue(psiClass != null);
final PsiMethod[] methodsByName = psiClass.findMethodsByName(methodSignature.getName(), true);
for (PsiMethod psiMethod : methodsByName) {
if (MethodSignatureUtil.areSignaturesEqual(getMethodSignature(psiMethod, psiClass, psiMethod.getContainingClass()), methodSignature)) {
method = psiMethod;
break;
}
}
LOG.assertTrue(method != null);
PsiType methodReturnType = method.getReturnType();
if (methodReturnType != null && methodReturnType != PsiType.VOID) {
methodReturnType = resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodReturnType));
final PsiElement body = lambdaExpression.getBody();
if (body instanceof PsiCodeBlock) {
final PsiCodeBlock block = (PsiCodeBlock)body;
for (PsiStatement statement : block.getStatements()) {
if (statement instanceof PsiReturnStatement) {
final PsiExpression returnValue = ((PsiReturnStatement)statement).getReturnValue();
if (returnValue != null) {
if (!checkReturnTypeAssignability(returnValue.getType(), parameterTypes, lambdaExpression, methodReturnType)) return false;
}
}
}
} else if (body instanceof PsiExpression) {
return checkReturnTypeAssignability(((PsiExpression)body).getType(), parameterTypes, lambdaExpression, methodReturnType);
}
}
return true;
}
private static boolean checkReturnTypeAssignability(PsiType lambdaReturnType, PsiType[] parameterTypes, PsiLambdaExpression lambdaExpression, PsiType methodReturnType) {
if (lambdaReturnType instanceof PsiLambdaParameterType) {
final PsiParameter parameter = ((PsiLambdaParameterType)lambdaReturnType).getParameter();
final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter);
if (parameterIndex > -1) {
lambdaReturnType = parameterTypes[parameterIndex];
}
}
return lambdaReturnType != null && methodReturnType.isAssignableFrom(lambdaReturnType);
}
}
@@ -70,4 +70,8 @@ public class PsiLambdaParameterType extends PsiType {
public PsiType[] getSuperTypes() {
return PsiType.EMPTY_ARRAY;
}
public PsiParameter getParameter() {
return myParameter;
}
}
@@ -690,6 +690,12 @@ public final class PsiUtil extends PsiUtilCore {
return file;
}
public static boolean isAnnotationMethod(PsiElement element) {
if (!(element instanceof PsiAnnotationMethod)) return false;
PsiClass psiClass = ((PsiAnnotationMethod)element).getContainingClass();
return psiClass != null && psiClass.isAnnotationType();
}
private static class TypeParameterIterator implements Iterator<PsiTypeParameter> {
private int myIndex;
private PsiTypeParameterListOwner myCurrentOwner;
@@ -96,7 +96,7 @@ public class PsiImplUtil {
if (resolved != null) {
PsiMethod[] methods = ((PsiClass)resolved).getMethods();
for (PsiMethod method : methods) {
if (method instanceof PsiAnnotationMethod && Comparing.equal(method.getName(), attributeName)) {
if (PsiUtil.isAnnotationMethod(method) && Comparing.equal(method.getName(), attributeName)) {
return ((PsiAnnotationMethod)method).getDefaultValue();
}
}
@@ -0,0 +1,3 @@
public class Util {
@Foo(val=<caret>) void foo(String[] args) { }
}
@@ -0,0 +1,17 @@
interface I1 {
void m();
}
interface I2<X> {
X m();
}
class Ambiguity1 {
static void m(I1 i1) {}
static <T> void m(I2<T> i2) {}
{
m<error descr="Ambiguous method call: both 'Ambiguity1.m(I1)' and 'Ambiguity1.m(I2<Object>)' match">(()->{throw new AssertionError();})</error>;
}
}
@@ -0,0 +1,27 @@
interface I {
void m();
}
interface I1 {
int m();
}
interface I2 {
String m();
}
interface I3<A> {
A m();
}
class AmbiguityRawGenerics {
void foo(I s) { }
void foo(I1 s) { }
void foo(I2 s) { }
<Z> void foo(I3<Z> s) { }
void bar() {
foo<error descr="Ambiguous method call: both 'AmbiguityRawGenerics.foo(I)' and 'AmbiguityRawGenerics.foo(I1)' match">(()-> { throw new RuntimeException(); })</error>;
}
}
@@ -0,0 +1,28 @@
interface I {
void m();
}
interface I1<A> {
void m(A a);
}
interface I2<A> {
void m(A a1, A a2);
}
interface IV<A> {
void m(A... as);
}
class AmbiguityVarargs {
void foo(I s) { }
void foo(I1<String> s) { }
void foo(I2<String> s) { }
void foo(IV<String> s) { }
void test() {
foo(()->{});
foo<error descr="Ambiguous method call: both 'AmbiguityVarargs.foo(I1<String>)' and 'AmbiguityVarargs.foo(IV<String>)' match">((a1) -> {})</error>;
foo((a1, a2)->{});
}
}
@@ -0,0 +1,27 @@
interface I {
void m(int x);
}
interface I1 {
int m();
}
class CastInContexts {
void m(I s) { }
void assignment() {
I i1 = (I)(x-> { System.out.println(); });
I i2 = (I)((x-> { System.out.println(); }));
}
void method() {
m((I)(x-> { System.out.println(); }));
}
I returnContext() {
return (I)(x -> { System.out.println(); });
}
{
int i = <error descr="Inconvertible types; cannot cast '<lambda expression>' to 'int'">(int) ()-> 1</error>;
}
}
@@ -0,0 +1,49 @@
interface I {
void m(int x);
}
class InContexts {
void m(I s) { }
void assignment() {
I s1 = (x-> { System.out.println(); });
I s2 = ((x-> { System.out.println(); }));
}
void method() {
m((x-> { System.out.println(); }));
m(((x-> { System.out.println(); })));
}
I returnContext() {
return (x -> {System.out.println();});
}
}
interface I1<A, B> {
B foo(A a);
}
class ValueLambdaInContext {
<Z> void m1(I1<String, Z> i) { }
<Z> void m2(I1<String, I1<String, Z>> i) { }
void exprMethod() {
m1(s1 -> 1);
m2(s1 -> s2 -> 1);
}
void exprAssignment() {
I1<String, Integer> in1 = s1 -> 1;
I1<String, I1<String, Integer>> in2 = s1 -> s2 -> 1;
}
void statementMethod() {
m1(s1 -> { return 1; });
m2(s1 -> { return s2 -> { return 1; }; });
}
void statementAssignment() {
I1<String, Integer> in1 = s1 -> { return 1; };
I1<String, I1<String, Integer>> in2 = s1 -> { return s2 -> { return 1; }; };
}
}
@@ -0,0 +1,29 @@
interface I<K, V> {
public V put(K k, V v);
}
interface A<T>{}
interface B<L> extends A<L>{}
interface SameArgsI<T> {
T same(T a, T b);
}
class InferenceFromArgs {
private static <E> void bar(A<? extends E> a, I<? super E, Integer> i) { }
void foo(B<Integer> b) {
bar(b, (k, v) -> {int i = k; int j = v; return Math.max(i, j);});
}
public static <T> SameArgsI<T> max() {
return (a, b) -> b;
}
}
@@ -0,0 +1,17 @@
interface I<A, B> {
B foo(A a);
}
class NoInferenceResult {
<A, B> I<A, B> m(I<A, B> f) { return null; }
<T> void m1(T t) { }
void test() {
m<error descr="'m(I<java.lang.Object,java.lang.Object>)' in 'NoInferenceResult' cannot be applied to '(<lambda expression>)'">((String s1) -> (String s2) -> s1 + s2)</error>;
m((String s1) -> s1.length());
m((String s1) -> s1);
m1<error descr="'m1(java.lang.Object)' in 'NoInferenceResult' cannot be applied to '(<lambda expression>)'">(() -> { })</error>;
}
}
@@ -0,0 +1,72 @@
class ReturnTypeIncompatibility {
interface I1<T extends Number> {
T m(Integer x);
}
interface I2<L extends String> {
L m(Integer x);
}
interface I3<K> {
void m(Integer x);
}
static <P extends Number> void call(I1<P> i1) {
i1.m(1);
}
static <P extends String> void call(I2<P> i2) {
i2.m(2);
}
static <Q> void call(I3<Q> i3) {
i3.m(3);
}
public static void main(String[] args) {
call(i-> {return i;});
}
}
class ReturnTypeCompatibility {
interface I1<T extends Number> {
T m(T x);
}
interface I2<L extends String> {
L m(L x);
}
interface I3<K> {
void m(K x);
}
static <P extends Number> void call(I1<P> i1) {
i1.m(null);
}
static <P extends String> void call(I2<P> i2) {
i2.m(null);
}
static <Q> void call(I3<Q> i3) {
i3.m(null);
}
public static void main(String[] args) {
<error descr="Cannot resolve method 'call(<lambda expression>)'">call</error>(i-> {return i;});
}
}
class ReturnTypeChecks1 {
interface I<K extends Number, V extends Number> {
V m(K k);
}
I<Integer, Integer> accepted = i -> { return i; };
<error descr="Incompatible types. Found: '<lambda expression>', required: 'ReturnTypeChecks1.I<java.lang.Double,java.lang.Integer>'">I<Double, Integer> rejected = i -> { return i; };</error>
}
@@ -0,0 +1,17 @@
class TypeArgsConsistency {
interface I<T> {
T m(int i, int j);
}
static void foo(I<Integer> s) { }
static <X> I<X> bar(I<X> s) { return null; }
{
I<Integer> i1 = (i, j) -> i + j;
foo((i, j) -> i + j);
I<Integer> i2 = bar((i, j) -> i + j);
I<Integer> i3 = bar<error descr="'bar(TypeArgsConsistency.I<java.lang.Integer>)' in 'TypeArgsConsistency' cannot be applied to '(<lambda expression>)'">((i, j) -> "" + i + j)</error>;
}
}
@@ -0,0 +1,20 @@
class WildcardBounds {
interface I<T> {
T foo(T a, T b);
}
void m1(I<? extends String> f1) {}
void m2(I<? super String> f2) {}
void m3(I<?> f3) {}
I<? extends String> f1 = (a, b) -> a;
I<? super String> f2 = (a, b) -> a;
I<?> f3 = (a, b) -> a;
{
m1((a, b) -> a);
m2((a, b) -> a);
m3((a, b) -> a);
}
}
@@ -16,4 +16,20 @@ class Foo {
bar((int i) -> {System.out.println(i);});
}
void bar(I i){}
}
class ReturnTypeCompatibility {
interface I1<L> {
L m(L x);
}
static <P> void call(I1<P> i2) {
i2.m(null);
}
public static void main(String[] args) {
call((String i)->{ return i;});
call<error descr="'call(ReturnTypeCompatibility.I1<java.lang.Object>)' in 'ReturnTypeCompatibility' cannot be applied to '(<lambda expression>)'">((int i)->{ return i;})</error>;
}
}
@@ -1,3 +1,5 @@
import java.lang.Integer;
interface I {
void m(int i);
}
@@ -18,6 +20,14 @@ class Foo {
<error descr="Incompatible types. Found: 'java.lang.String', required: 'int'">int i = ab;</error>
};
{
A<String> a1;
a1 = (ab)->{
String s = ab;
<error descr="Incompatible types. Found: 'java.lang.String', required: 'int'">int i = ab;</error>
};
}
A<Integer> bazz() {
bar((o) -> {
String s = o;
@@ -33,3 +43,32 @@ class Foo {
void bar(A<String> a){}
}
class CastInference {
public interface I1<X> {
X m();
}
public interface I2<X> {
X m();
}
public static <X> void foo(I1<X> s) {}
public static <X> void foo(I2<X> s) {}
public static void main(String[] args) {
foo((I1<Integer>)() -> 42);
I1<Integer> i1 = (I1<Integer>)() -> 42;
}
}
class WildcardBoundsUsage {
interface I<X> {
boolean foo(X x);
}
public I<Character> bar(I<? super Character> predicate) {
return null;
}
{
I<Character> i = bar(c -> c.compareTo('x') < 0);
}
}
@@ -45,6 +45,8 @@ import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiJavaFile
import junit.framework.Test
import junit.framework.TestSuite
/**
* @author peter
@@ -560,6 +562,7 @@ public interface Test {
assert myFixture.editor.caretModel.offset == offset + 1
joinAutopopup()
joinCompletion()
assert !lookup.calculating
assertContains "iterable"
assertEquals 'iterable', lookup.currentItem.lookupString
@@ -567,12 +570,14 @@ public interface Test {
assert myFixture.editor.caretModel.offset == offset
joinAutopopup()
joinCompletion()
assert !lookup.calculating
assertContains "if", "iterable", "int"
assertEquals 'iterable', lookup.currentItem.lookupString
edt { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_MOVE_CARET_LEFT) }
joinAutopopup()
joinCompletion()
assert !lookup.calculating
assert lookup.items.size() > 3
for (i in 0.."iter".size()) {
@@ -270,6 +270,20 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase {
assert "B" in myFixture.lookupElementStrings
}
public void testAnnoParameterValue() throws Exception {
configure()
def strings = myFixture.lookupElementStrings
assert 'AssertionError' in strings
assert !('enum' in strings)
assert !('final' in strings)
assert !('equals' in strings)
assert !('new' in strings)
assert !('null' in strings)
assert !('public' in strings)
assert !('super' in strings)
assert !('null' in strings)
}
public void testAfterInstanceof() throws Exception {
configureByFile("AfterInstanceof.java");
assert "A" in myFixture.lookupElementStrings
@@ -37,6 +37,48 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testReturnTypeCompatibility() throws Exception {
doTest();
}
public void testTypeArgsConsistency() throws Exception {
doTest();
}
public void testWildcardBounds() throws Exception {
doTest();
}
public void testAmbiguity1() throws Exception {
doTest();
}
public void testAmbiguityVarargs() throws Exception {
doTest();
}
public void testAmbiguityRawGenerics() throws Exception {
doTest();
}
public void testNoInferenceResult() throws Exception {
doTest();
}
public void testInferenceFromArgs() throws Exception {
doTest();
}
public void testInContexts() throws Exception {
doTest();
}
public void testCastInContexts() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
}
@@ -17,6 +17,7 @@ package com.intellij.codeInsight.completion.util;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
/**
@@ -39,16 +40,16 @@ public class MethodParenthesesHandler extends ParenthesesInsertHandler<LookupEle
public static boolean hasParams(LookupElement item, LookupElement[] allItems, final boolean overloadsMatter, final PsiMethod method) {
boolean hasParams = method.getParameterList().getParametersCount() > 0;
if (overloadsMatter){
hasParams |= hasOverloads(item, allItems, method);
hasParams |= hasOverloads(allItems, method);
}
return hasParams;
}
private static boolean hasOverloads(LookupElement item, LookupElement[] allItems, final PsiMethod method) {
private static boolean hasOverloads(LookupElement[] allItems, final PsiMethod method) {
String name = method.getName();
for (LookupElement item1 : allItems) {
final Object o = item1.getObject();
if (item.getObject() != o && o instanceof PsiMethod && ((PsiMethod)o).getName().equals(name)) {
for (LookupElement another : allItems) {
final PsiElement element = another.getPsiElement();
if (method != element && element instanceof PsiMethod && ((PsiMethod)element).getName().equals(name)) {
return true;
}
}
@@ -19,7 +19,7 @@ import java.util.Map;
* Date: 10/7/11
*/
public class BuildDataManager implements StorageOwner {
private static final int VERSION = 7;
private static final int VERSION = 8;
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.storage.BuildDataManager");
private static final String SRC_TO_OUTPUTS_STORAGE = "src-out";
private static final String SRC_TO_FORM_STORAGE = "src-form";
@@ -780,7 +780,7 @@ public class Mappings {
final Util myFuture;
final Util myPresent;
final boolean myPreprocessOnly;
final boolean myEasyMode; // true means: no need to search for affected files, only preprocess data for integrate
private class DelayedWorks {
class Triple {
@@ -820,7 +820,7 @@ public class Mappings {
myQueue.add(new Triple(ownerClass, changedField, future));
}
boolean doWork(final Collection<File> affectedFiles) {
boolean doWork(@NotNull final Collection<File> affectedFiles) {
if (!myQueue.isEmpty()) {
debug("Starting delayed works.");
@@ -888,7 +888,7 @@ public class Mappings {
myFuture = null;
myPresent = null;
myPreprocessOnly = true;
myEasyMode = true;
delta.myIsRebuild = true;
}
@@ -907,7 +907,7 @@ public class Mappings {
myFuture = new Util(delta);
myPresent = new Util();
myPreprocessOnly = true;
myEasyMode = true;
}
private Differential(final Mappings delta,
@@ -931,22 +931,24 @@ public class Mappings {
myFuture = new Util(delta);
myPresent = new Util();
myPreprocessOnly = false;
myEasyMode = false;
}
private void processDisappearedClasses() {
myDelta.compensateRemovedContent(myFilesToCompile);
final Collection<String> removed = myDelta.myRemovedFiles;
if (!myEasyMode) {
final Collection<String> removed = myDelta.myRemovedFiles;
if (removed != null) {
for (final String file : removed) {
final Collection<ClassRepr> classes = mySourceFileToClasses.get(myContext.get(file));
if (removed != null) {
for (final String file : removed) {
final Collection<ClassRepr> classes = mySourceFileToClasses.get(myContext.get(file));
if (classes != null) {
for (ClassRepr c : classes) {
debug("Affecting usages of removed class ", c.name);
affectAll(c.name, myAffectedFiles, myFilter);
if (classes != null) {
for (ClassRepr c : classes) {
debug("Affecting usages of removed class ", c.name);
affectAll(c.name, myAffectedFiles, myFilter);
}
}
}
}
@@ -1542,8 +1544,8 @@ public class Mappings {
}
}
if (myPreprocessOnly) {
return false;
if (myEasyMode) {
continue;
}
myPresent.appendDependents(it, state.myDependants);
@@ -1657,7 +1659,7 @@ public class Mappings {
}
debug("End of changed classes processing");
return true;
return !myEasyMode;
}
private void processRemovedClases(final DiffState state) {
@@ -1671,7 +1673,7 @@ public class Mappings {
myDelta.myChangedFiles.add(fileName);
}
if (!myPreprocessOnly) {
if (!myEasyMode) {
myPresent.appendDependents(c, state.myDependants);
debug("Adding usages of class ", c.name);
state.myAffectedUsages.add(c.createUsage());
@@ -1690,7 +1692,7 @@ public class Mappings {
myDelta.registerAddedSuperClass(c.name, sup);
}
if (!myPreprocessOnly) {
if (!myEasyMode) {
final TIntHashSet depClasses = myClassToClassDependency.get(c.name);
if (depClasses != null) {
@@ -1799,7 +1801,7 @@ public class Mappings {
}
debug("Begin of Differentiate:");
debug("Easy mode: ", myPreprocessOnly);
debug("Easy mode: ", myEasyMode);
processDisappearedClasses();
@@ -1818,34 +1820,34 @@ public class Mappings {
final Set<ClassRepr> pastClasses = (Set<ClassRepr>)mySourceFileToClasses.get(fileName);
final DiffState state = new DiffState(Difference.make(pastClasses, classes));
if (!processChangedClasses(state) && !myPreprocessOnly) {
// turning non-incremental
return false;
if (!processChangedClasses(state)) {
if (!myEasyMode) {
// turning non-incremental
return false;
}
}
processRemovedClases(state);
processAddedClasses(state);
if (!myPreprocessOnly) {
if (!myEasyMode) {
calculateAffectedFiles(state);
}
}
debug("End of Differentiate.");
if (!myPreprocessOnly) {
final Collection<String> removed = myDelta.myRemovedFiles;
if (removed != null) {
for (final String r : removed) {
myAffectedFiles.remove(new File(r));
}
}
return myDelayedWorks.doWork(myAffectedFiles);
}
else {
if (myEasyMode) {
return false;
}
final Collection<String> removed = myDelta.myRemovedFiles;
if (removed != null) {
for (final String r : removed) {
myAffectedFiles.remove(new File(r));
}
}
return myDelayedWorks.doWork(myAffectedFiles);
}
}
}
@@ -308,7 +308,7 @@ public class JarHandlerBase {
public FileAttributes getAttributes(@NotNull final VirtualFile file) {
synchronized (lock) {
final JarFile.JarEntry entry = convertToEntry(file);
return entry != null ? new FileAttributes(entry.isDirectory(), false, false, entry.getSize(), entry.getTime(), false) : null;
return entry != null ? new FileAttributes(entry.isDirectory(), false, false, false, entry.getSize(), entry.getTime(), false) : null;
}
}
}
@@ -16,9 +16,11 @@
package com.intellij.codeInsight.lookup;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.navigation.PsiElementNavigationItem;
import com.intellij.openapi.util.ClassConditionKey;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -47,6 +49,21 @@ public abstract class LookupElement extends UserDataHolderBase {
return this;
}
@Nullable
public PsiElement getPsiElement() {
Object o = getObject();
if (o instanceof PsiElement) {
return (PsiElement)o;
}
if (o instanceof ResolveResult) {
return ((ResolveResult)o).getElement();
}
if (o instanceof PsiElementNavigationItem) {
return ((PsiElementNavigationItem)o).getTargetElement();
}
return null;
}
public boolean isValid() {
final Object object = getObject();
if (object instanceof PsiElement) {
@@ -652,6 +652,11 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement
hideAutopopupIfMeaningless();
CompletionPhase oldPhase = CompletionServiceImpl.getCompletionPhase();
if (oldPhase instanceof CompletionPhase.CommittingDocuments) {
((CompletionPhase.CommittingDocuments)oldPhase).replaced = true;
}
final CompletionPhase.CommittingDocuments phase = new CompletionPhase.CommittingDocuments(this, myEditor);
CompletionServiceImpl.setCompletionPhase(phase);
@@ -1,275 +1,266 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.LookupValueWithPsiElement;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.navigation.PsiElementNavigationItem;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.filters.TrueFilter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.patterns.PlatformPatterns.character;
public class CompletionUtil {
static final Key<OffsetTranslator> RANGE_TRANSLATION = Key.create("completion.rangeTranslation");
public static final Key<TailType> TAIL_TYPE_ATTR = LookupItem.TAIL_TYPE_ATTR;
private static final CompletionData ourGenericCompletionData = new CompletionData() {
{
final CompletionVariant variant = new CompletionVariant(PsiElement.class, TrueFilter.INSTANCE);
variant.addCompletionFilter(TrueFilter.INSTANCE, TailType.NONE);
registerVariant(variant);
}
};
private static final HashMap<FileType, NotNullLazyValue<CompletionData>> ourCustomCompletionDatas = new HashMap<FileType, NotNullLazyValue<CompletionData>>();
public static final @NonNls String DUMMY_IDENTIFIER = CompletionInitializationContext.DUMMY_IDENTIFIER;
public static final @NonNls String DUMMY_IDENTIFIER_TRIMMED = DUMMY_IDENTIFIER.trim();
public static boolean startsWith(String text, String prefix) {
//if (text.length() <= prefix.length()) return false;
return toLowerCase(text).startsWith(toLowerCase(prefix));
}
private static String toLowerCase(String text) {
CodeInsightSettings settings = CodeInsightSettings.getInstance();
switch (settings.COMPLETION_CASE_SENSITIVE) {
case CodeInsightSettings.NONE:
return text.toLowerCase();
case CodeInsightSettings.FIRST_LETTER: {
StringBuffer buffer = new StringBuffer();
buffer.append(text.toLowerCase());
if (buffer.length() > 0) {
buffer.setCharAt(0, text.charAt(0));
}
return buffer.toString();
}
default:
return text;
}
}
@Nullable
public static CompletionData getCompletionDataByElement(@Nullable final PsiElement position, @NotNull PsiFile originalFile) {
if (position == null) return null;
final FileType fileType = position.getParent().getLanguage().getAssociatedFileType();
if (fileType != null) {
final CompletionData mainData = getCompletionDataByFileType(fileType);
if (mainData != null) {
return mainData;
}
}
final CompletionData mainData = getCompletionDataByFileType(originalFile.getFileType());
return mainData != null ? mainData : ourGenericCompletionData;
}
/** @see CompletionDataEP */
@Deprecated
public static void registerCompletionData(FileType fileType, NotNullLazyValue<CompletionData> completionData) {
ourCustomCompletionDatas.put(fileType, completionData);
}
/** @see CompletionDataEP */
@Deprecated
public static void registerCompletionData(FileType fileType, final CompletionData completionData) {
registerCompletionData(fileType, new NotNullLazyValue<CompletionData>() {
@NotNull
protected CompletionData compute() {
return completionData;
}
});
}
@Nullable
public static CompletionData getCompletionDataByFileType(FileType fileType) {
for(CompletionDataEP ep: Extensions.getExtensions(CompletionDataEP.EP_NAME)) {
if (ep.fileType.equals(fileType.getName())) {
return ep.getHandler();
}
}
final NotNullLazyValue<CompletionData> lazyValue = ourCustomCompletionDatas.get(fileType);
return lazyValue == null ? null : lazyValue.getValue();
}
public static boolean shouldShowFeature(final CompletionParameters parameters, @NonNls final String id) {
if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(id, parameters.getPosition().getProject())) {
FeatureUsageTracker.getInstance().triggerFeatureShown(id);
return true;
}
return false;
}
public static String findJavaIdentifierPrefix(CompletionParameters parameters) {
return findJavaIdentifierPrefix(parameters.getPosition(), parameters.getOffset());
}
public static String findJavaIdentifierPrefix(final PsiElement insertedElement, final int offset) {
return findIdentifierPrefix(insertedElement, offset, character().javaIdentifierPart(), character().javaIdentifierStart());
}
public static String findReferenceOrAlphanumericPrefix(CompletionParameters parameters) {
String prefix = findReferencePrefix(parameters);
return prefix == null ? findAlphanumericPrefix(parameters) : prefix;
}
public static String findAlphanumericPrefix(CompletionParameters parameters) {
return findIdentifierPrefix(parameters.getPosition().getContainingFile(), parameters.getOffset(), character().letterOrDigit(), character().letterOrDigit());
}
public static String findIdentifierPrefix(PsiElement insertedElement, int offset, ElementPattern<Character> idPart,
ElementPattern<Character> idStart) {
if(insertedElement == null) return "";
final String text = insertedElement.getText();
final int offsetInElement = offset - insertedElement.getTextRange().getStartOffset();
int start = offsetInElement - 1;
while (start >=0 ) {
if (!idPart.accepts(text.charAt(start))) break;
--start;
}
while (start + 1 < offsetInElement && !idStart.accepts(text.charAt(start + 1))) {
start++;
}
return text.substring(start + 1, offsetInElement).trim();
}
@Nullable
public static String findReferencePrefix(CompletionParameters parameters) {
return CompletionData.getReferencePrefix(parameters.getPosition(), parameters.getOffset());
}
static InsertionContext emulateInsertion(InsertionContext oldContext, int newStart, final LookupElement item) {
final InsertionContext newContext = newContext(oldContext, item);
emulateInsertion(item, newStart, newContext);
return newContext;
}
private static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement) {
final Editor editor = oldContext.getEditor();
return new InsertionContext(new OffsetMap(editor.getDocument()), Lookup.AUTO_INSERT_SELECT_CHAR, new LookupElement[]{forElement}, oldContext.getFile(), editor,
oldContext.shouldAddCompletionChar());
}
public static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement, int startOffset, int tailOffset) {
final InsertionContext context = newContext(oldContext, forElement);
setOffsets(context, startOffset, tailOffset);
return context;
}
public static void emulateInsertion(LookupElement item, int offset, InsertionContext context) {
setOffsets(context, offset, offset);
final Editor editor = context.getEditor();
final Document document = editor.getDocument();
final String lookupString = item.getLookupString();
document.insertString(offset, lookupString);
editor.getCaretModel().moveToOffset(context.getTailOffset());
PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
item.handleInsert(context);
}
private static void setOffsets(InsertionContext context, int offset, final int tailOffset) {
final OffsetMap offsetMap = context.getOffsetMap();
offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset);
offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, tailOffset);
offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, tailOffset);
context.setTailOffset(tailOffset);
}
@Nullable
public static PsiElement getTargetElement(LookupElement lookupElement) {
Object object = lookupElement.getObject();
if (object instanceof ResolveResult) {
object = ((ResolveResult)object).getElement();
}
if (object instanceof PsiElement) {
return getOriginalElement((PsiElement)object);
}
if (object instanceof PsiElementNavigationItem) {
return ((PsiElementNavigationItem)object).getTargetElement();
}
if (object instanceof LookupValueWithPsiElement) {
final PsiElement element = ((LookupValueWithPsiElement)object).getElement();
if (element != null) return getOriginalElement(element);
}
return null;
}
@Nullable
public static <T extends PsiElement> T getOriginalElement(@NotNull T psi) {
final PsiFile file = psi.getContainingFile();
if (file != null && file != file.getOriginalFile() && psi.getTextRange() != null) {
TextRange range = psi.getTextRange();
Integer start = range.getStartOffset();
Integer end = range.getEndOffset();
final Document document = file.getViewProvider().getDocument();
if (document != null) {
final OffsetTranslator translator = document.getUserData(RANGE_TRANSLATION);
if (translator != null) {
start = translator.translateOffset(start);
end = translator.translateOffset(end);
if (start == null || end == null) {
return null;
}
}
}
//noinspection unchecked
return (T)PsiTreeUtil.findElementOfClassAtRange(file.getOriginalFile(), start, end, psi.getClass());
}
return psi;
}
@NotNull
public static <T extends PsiElement> T getOriginalOrSelf(@NotNull T psi) {
final T element = getOriginalElement(psi);
return element == null ? psi : element;
}
}
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.LookupValueWithPsiElement;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.filters.TrueFilter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.patterns.PlatformPatterns.character;
public class CompletionUtil {
static final Key<OffsetTranslator> RANGE_TRANSLATION = Key.create("completion.rangeTranslation");
public static final Key<TailType> TAIL_TYPE_ATTR = LookupItem.TAIL_TYPE_ATTR;
private static final CompletionData ourGenericCompletionData = new CompletionData() {
{
final CompletionVariant variant = new CompletionVariant(PsiElement.class, TrueFilter.INSTANCE);
variant.addCompletionFilter(TrueFilter.INSTANCE, TailType.NONE);
registerVariant(variant);
}
};
private static final HashMap<FileType, NotNullLazyValue<CompletionData>> ourCustomCompletionDatas = new HashMap<FileType, NotNullLazyValue<CompletionData>>();
public static final @NonNls String DUMMY_IDENTIFIER = CompletionInitializationContext.DUMMY_IDENTIFIER;
public static final @NonNls String DUMMY_IDENTIFIER_TRIMMED = DUMMY_IDENTIFIER.trim();
public static boolean startsWith(String text, String prefix) {
//if (text.length() <= prefix.length()) return false;
return toLowerCase(text).startsWith(toLowerCase(prefix));
}
private static String toLowerCase(String text) {
CodeInsightSettings settings = CodeInsightSettings.getInstance();
switch (settings.COMPLETION_CASE_SENSITIVE) {
case CodeInsightSettings.NONE:
return text.toLowerCase();
case CodeInsightSettings.FIRST_LETTER: {
StringBuffer buffer = new StringBuffer();
buffer.append(text.toLowerCase());
if (buffer.length() > 0) {
buffer.setCharAt(0, text.charAt(0));
}
return buffer.toString();
}
default:
return text;
}
}
@Nullable
public static CompletionData getCompletionDataByElement(@Nullable final PsiElement position, @NotNull PsiFile originalFile) {
if (position == null) return null;
final FileType fileType = position.getParent().getLanguage().getAssociatedFileType();
if (fileType != null) {
final CompletionData mainData = getCompletionDataByFileType(fileType);
if (mainData != null) {
return mainData;
}
}
final CompletionData mainData = getCompletionDataByFileType(originalFile.getFileType());
return mainData != null ? mainData : ourGenericCompletionData;
}
/** @see CompletionDataEP */
@Deprecated
public static void registerCompletionData(FileType fileType, NotNullLazyValue<CompletionData> completionData) {
ourCustomCompletionDatas.put(fileType, completionData);
}
/** @see CompletionDataEP */
@Deprecated
public static void registerCompletionData(FileType fileType, final CompletionData completionData) {
registerCompletionData(fileType, new NotNullLazyValue<CompletionData>() {
@NotNull
protected CompletionData compute() {
return completionData;
}
});
}
@Nullable
public static CompletionData getCompletionDataByFileType(FileType fileType) {
for(CompletionDataEP ep: Extensions.getExtensions(CompletionDataEP.EP_NAME)) {
if (ep.fileType.equals(fileType.getName())) {
return ep.getHandler();
}
}
final NotNullLazyValue<CompletionData> lazyValue = ourCustomCompletionDatas.get(fileType);
return lazyValue == null ? null : lazyValue.getValue();
}
public static boolean shouldShowFeature(final CompletionParameters parameters, @NonNls final String id) {
if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(id, parameters.getPosition().getProject())) {
FeatureUsageTracker.getInstance().triggerFeatureShown(id);
return true;
}
return false;
}
public static String findJavaIdentifierPrefix(CompletionParameters parameters) {
return findJavaIdentifierPrefix(parameters.getPosition(), parameters.getOffset());
}
public static String findJavaIdentifierPrefix(final PsiElement insertedElement, final int offset) {
return findIdentifierPrefix(insertedElement, offset, character().javaIdentifierPart(), character().javaIdentifierStart());
}
public static String findReferenceOrAlphanumericPrefix(CompletionParameters parameters) {
String prefix = findReferencePrefix(parameters);
return prefix == null ? findAlphanumericPrefix(parameters) : prefix;
}
public static String findAlphanumericPrefix(CompletionParameters parameters) {
return findIdentifierPrefix(parameters.getPosition().getContainingFile(), parameters.getOffset(), character().letterOrDigit(), character().letterOrDigit());
}
public static String findIdentifierPrefix(PsiElement insertedElement, int offset, ElementPattern<Character> idPart,
ElementPattern<Character> idStart) {
if(insertedElement == null) return "";
final String text = insertedElement.getText();
final int offsetInElement = offset - insertedElement.getTextRange().getStartOffset();
int start = offsetInElement - 1;
while (start >=0 ) {
if (!idPart.accepts(text.charAt(start))) break;
--start;
}
while (start + 1 < offsetInElement && !idStart.accepts(text.charAt(start + 1))) {
start++;
}
return text.substring(start + 1, offsetInElement).trim();
}
@Nullable
public static String findReferencePrefix(CompletionParameters parameters) {
return CompletionData.getReferencePrefix(parameters.getPosition(), parameters.getOffset());
}
static InsertionContext emulateInsertion(InsertionContext oldContext, int newStart, final LookupElement item) {
final InsertionContext newContext = newContext(oldContext, item);
emulateInsertion(item, newStart, newContext);
return newContext;
}
private static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement) {
final Editor editor = oldContext.getEditor();
return new InsertionContext(new OffsetMap(editor.getDocument()), Lookup.AUTO_INSERT_SELECT_CHAR, new LookupElement[]{forElement}, oldContext.getFile(), editor,
oldContext.shouldAddCompletionChar());
}
public static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement, int startOffset, int tailOffset) {
final InsertionContext context = newContext(oldContext, forElement);
setOffsets(context, startOffset, tailOffset);
return context;
}
public static void emulateInsertion(LookupElement item, int offset, InsertionContext context) {
setOffsets(context, offset, offset);
final Editor editor = context.getEditor();
final Document document = editor.getDocument();
final String lookupString = item.getLookupString();
document.insertString(offset, lookupString);
editor.getCaretModel().moveToOffset(context.getTailOffset());
PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
item.handleInsert(context);
}
private static void setOffsets(InsertionContext context, int offset, final int tailOffset) {
final OffsetMap offsetMap = context.getOffsetMap();
offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset);
offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, tailOffset);
offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, tailOffset);
context.setTailOffset(tailOffset);
}
@Nullable
public static PsiElement getTargetElement(LookupElement lookupElement) {
PsiElement psiElement = lookupElement.getPsiElement();
if (psiElement != null) {
return getOriginalElement(psiElement);
}
Object object = lookupElement.getObject();
if (object instanceof LookupValueWithPsiElement) {
final PsiElement element = ((LookupValueWithPsiElement)object).getElement();
if (element != null) return getOriginalElement(element);
}
return null;
}
@Nullable
public static <T extends PsiElement> T getOriginalElement(@NotNull T psi) {
final PsiFile file = psi.getContainingFile();
if (file != null && file != file.getOriginalFile() && psi.getTextRange() != null) {
TextRange range = psi.getTextRange();
Integer start = range.getStartOffset();
Integer end = range.getEndOffset();
final Document document = file.getViewProvider().getDocument();
if (document != null) {
final OffsetTranslator translator = document.getUserData(RANGE_TRANSLATION);
if (translator != null) {
start = translator.translateOffset(start);
end = translator.translateOffset(end);
if (start == null || end == null) {
return null;
}
}
}
//noinspection unchecked
return (T)PsiTreeUtil.findElementOfClassAtRange(file.getOriginalFile(), start, end, psi.getClass());
}
return psi;
}
@NotNull
public static <T extends PsiElement> T getOriginalOrSelf(@NotNull T psi) {
final T element = getOriginalElement(psi);
return element == null ? psi : element;
}
}
@@ -31,8 +31,7 @@ public class LookupElementProximityWeigher extends CompletionWeigher {
return PsiProximityComparator.getProximity(new NullableComputable<PsiElement>() {
@Override
public PsiElement compute() {
Object object = item.getObject();
return object instanceof PsiElement ? (PsiElement)object : null;
return item.getPsiElement();
}
}, location.getCompletionParameters().getPosition(), location.getProcessingContext());
}
@@ -73,14 +73,9 @@ public class DefaultLookupItemRenderer extends LookupElementRenderer<LookupItem>
return ((Iconable)o).getIcon(Iconable.ICON_FLAG_VISIBILITY);
}
if (o instanceof LookupValueWithPsiElement) {
o = ((LookupValueWithPsiElement)o).getElement();
}
if (o instanceof PsiElement) {
final PsiElement element = (PsiElement)o;
if (element.isValid()) {
return element.getIcon(Iconable.ICON_FLAG_VISIBILITY);
}
final PsiElement element = item.getPsiElement();
if (element != null && element.isValid()) {
return element.getIcon(Iconable.ICON_FLAG_VISIBILITY);
}
return null;
}
@@ -44,10 +44,7 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.ui.AutoScrollFromSourceHandler;
import com.intellij.ui.AutoScrollToSourceHandler;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.ui.*;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.ui.treeStructure.actions.CollapseAllAction;
import com.intellij.ui.treeStructure.actions.ExpandAllAction;
@@ -132,7 +129,7 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre
};
final DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode(treeStructure.getRootElement()));
myTree = new Tree(model);
myTree = new JBTreeWithHintProvider(model);
myTree.setRootVisible(showRootNode);
myTree.setShowsRootHandles(true);
@@ -15,6 +15,8 @@
*/
package com.intellij.openapi.diff;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
@@ -25,6 +27,7 @@ public interface DiffViewer {
JComponent getComponent();
@Nullable
JComponent getPreferredFocusedComponent();
int getContentsNumber();
@@ -25,6 +25,7 @@ import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileElement;
import com.intellij.openapi.fileChooser.PathChooserDialog;
import com.intellij.openapi.fileChooser.impl.FileChooserUtil;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
@@ -96,7 +97,7 @@ public class OpenFileAction extends AnAction implements DumbAware {
@NotNull final List<VirtualFile> result) {
for (final VirtualFile file : result) {
if (file.isDirectory()) {
ProjectUtil.openOrImport(file.getPath(), project, false);
FileChooserUtil.setLastOpenedFile(ProjectUtil.openOrImport(file.getPath(), project, false), file);
return;
}
@@ -106,7 +107,7 @@ public class OpenFileAction extends AnAction implements DumbAware {
IdeBundle.message("title.open.project"),
Messages.getQuestionIcon());
if (answer == 0) {
ProjectUtil.openOrImport(file.getPath(), project, false);
FileChooserUtil.setLastOpenedFile(ProjectUtil.openOrImport(file.getPath(), project, false), file);
return;
}
}
@@ -25,6 +25,7 @@ import com.intellij.openapi.diff.impl.external.DiscloseMultiRequest;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.content.Content;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@@ -121,6 +122,7 @@ public class CompositeDiffPanel implements DiffViewer {
return myUi.getComponent();
}
@Nullable
@Override
public JComponent getPreferredFocusedComponent() {
final Content[] contents = myUi.getContents();
@@ -77,7 +77,6 @@ import java.util.List;
public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSidesContainer {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.DiffPanelImpl");
public static final int DIFF_DIVIDER_POLYGONS_OFFSET = 3;
private final DiffSplitterI mySplitter;
private final DiffPanelOuterComponent myPanel;
@@ -111,7 +110,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
private CanNotCalculateDiffPanel myNotCalculateDiffPanel;
private final VisibleAreaListener myVisibleAreaListener;
public DiffPanelImpl(final Window owner, Project project, boolean enableToolbar, boolean horizontal) {
public DiffPanelImpl(final Window owner, Project project, boolean enableToolbar, boolean horizontal, int diffDividerPolygonsOffset) {
myProject = project;
myIsHorizontal = horizontal;
myOptions = new DiffPanelOptions(this);
@@ -130,7 +129,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
if (horizontal) {
mySplitter = new DiffSplitter(myLeftSide.getComponent(), myRightSide.getComponent(),
new DiffDividerPaint(this, FragmentSide.SIDE1, DIFF_DIVIDER_POLYGONS_OFFSET), myData);
new DiffDividerPaint(this, FragmentSide.SIDE1, diffDividerPolygonsOffset), myData);
}
else {
mySplitter = new HorizontalDiffSplitter(myLeftSide.getComponent(), myRightSide.getComponent());
@@ -335,7 +334,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
}
int[] getFragmentBeginnings(FragmentSide side) {
return getLineBlocks().getBegginings(side);
return getLineBlocks().getBeginnings(side);
}
public void dispose() {
@@ -369,6 +368,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
return getLineBlocks().getCount() > 0 || myNotCalculateDiffPanel != null;
}
@Nullable
public JComponent getPreferredFocusedComponent() {
return myCurrentSide.getFocusableComponent();
}
@@ -18,8 +18,10 @@ package com.intellij.openapi.diff.impl;
import com.intellij.openapi.diff.impl.highlighting.FragmentSide;
import com.intellij.openapi.diff.impl.splitter.LineBlocks;
import com.intellij.openapi.editor.Editor;
import org.jetbrains.annotations.Nullable;
public interface EditingSides {
@Nullable
Editor getEditor(FragmentSide side);
LineBlocks getLineBlocks();
}
@@ -21,6 +21,7 @@ import com.intellij.openapi.diff.DiffViewer;
import com.intellij.openapi.diff.DiffViewerType;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@@ -46,6 +47,7 @@ public class EmptyDiffViewer implements DiffViewer {
return wrapper;
}
@Nullable
@Override
public JComponent getPreferredFocusedComponent() {
return null;
@@ -38,6 +38,7 @@ import java.util.ArrayList;
import java.util.Arrays;
public class DiffManagerImpl extends DiffManager implements JDOMExternalizable {
public static final int FULL_DIFF_DIVIDER_POLYGONS_OFFSET = 3;
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.external.DiffManagerImpl");
private static final Externalizer<String> TOOL_PATH_UPDATE = new Externalizer<String>() {
@@ -137,7 +138,7 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable {
}
public DiffPanel createDiffPanel(Window window, Project project) {
return new DiffPanelImpl(window, project, true, true);
return new DiffPanelImpl(window, project, true, true, FULL_DIFF_DIVIDER_POLYGONS_OFFSET);
}
@Override
@@ -22,17 +22,22 @@ import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.diff.DiffBundle;
import com.intellij.openapi.diff.impl.incrementalMerge.Change;
import com.intellij.openapi.diff.impl.incrementalMerge.MergeList;
import com.intellij.openapi.diff.impl.util.DiffPanelOuterComponent;
import com.intellij.openapi.project.DumbAware;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FilteringIterator;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ApplyNonConflicts extends AnAction implements DumbAware {
public ApplyNonConflicts() {
@Nullable private final DiffPanelOuterComponent myDiffPanel;
public ApplyNonConflicts(@Nullable DiffPanelOuterComponent diffPanel) {
super(DiffBundle.message("merge.dialog.apply.all.non.conflicting.changes.action.name"), null, AllIcons.Diff.ApplyNotConflicts);
myDiffPanel = diffPanel;
}
public void actionPerformed(AnActionEvent e) {
@@ -41,6 +46,9 @@ public class ApplyNonConflicts extends AnAction implements DumbAware {
for (Change change : notConflicts) {
Change.apply(change, MergeList.BRANCH_SIDE);
}
if (myDiffPanel != null) {
myDiffPanel.requestScrollEditors();
}
}
public void update(AnActionEvent e) {
@@ -237,7 +237,7 @@ public class EditorPlace extends JComponent implements Disposable, EditorEx.Repa
return myEditor;
}
public void setDocument(Document document) {
public void setDocument(@Nullable Document document) {
if (document == getDocument()) return;
removeEditor();
myState.setDocument(document);
@@ -145,7 +145,7 @@ public class MergePanel2 implements DiffViewer {
toolbar.addAction(new OpenPartialDiffAction(1, 2, AllIcons.Diff.RightDiff));
toolbar.addAction(new OpenPartialDiffAction(0, 2, AllIcons.Diff.BranchDiff));
toolbar.addSeparator();
toolbar.addAction(new ApplyNonConflicts());
toolbar.addAction(new ApplyNonConflicts(myPanel));
toolbar.addSeparator();
toolbar.addAction(new MergeToolSettingsAction(getEditors()));
}
@@ -177,6 +177,7 @@ public class MergePanel2 implements DiffViewer {
/**
* @deprecated Because it references by index.
*/
@Nullable
@Deprecated
public Editor getEditor(int index) {
return getEditorPlace(index).getEditor();
@@ -265,20 +266,9 @@ public class MergePanel2 implements DiffViewer {
}
Editor editor = place.getEditor();
DiffEditorState editorState = place.getState();
((EditorEx)editor).setHighlighter(EditorHighlighterFactory.getInstance().
createEditorHighlighter(editorState.getFileType(), settings, editorState.getProject()));
}
public void setAdditionalLinesAndColumns(int lines, int columns) {
for (Editor editor : getEditors()) {
editor.getSettings().setAdditionalLinesCount(lines);
editor.getSettings().setAdditionalColumnsCount(columns);
}
}
public void setShowLineNumbers(boolean show) {
for (Editor editor : getEditors()) {
MergeToolEditorSetting.LINE_NUMBERS.apply(editor, show);
if (editor != null) {
((EditorEx)editor).setHighlighter(EditorHighlighterFactory.getInstance().
createEditorHighlighter(editorState.getFileType(), settings, editorState.getProject()));
}
}
@@ -339,6 +329,7 @@ public class MergePanel2 implements DiffViewer {
parent.setTitle(windowTitle);
}
@Nullable
private JDialog getDialogWrapperParent() {
Component panel = myPanel;
while (panel != null){
@@ -352,6 +343,7 @@ public class MergePanel2 implements DiffViewer {
return myPanel;
}
@Nullable
public JComponent getPreferredFocusedComponent() {
return getEditorPlace(1).getContentComponent();
}
@@ -372,6 +364,7 @@ public class MergePanel2 implements DiffViewer {
return true;
}
@Nullable
public MergeRequestImpl getMergeRequest() {
return (MergeRequestImpl)(myData instanceof MergeRequestImpl ? myData : null);
}
@@ -383,6 +376,7 @@ public class MergePanel2 implements DiffViewer {
mySide = side;
}
@Nullable
public Editor getEditor(FragmentSide side) {
return MergePanel2.this.getEditor(mySide.getIndex() + side.getIndex());
}
@@ -399,8 +393,8 @@ public class MergePanel2 implements DiffViewer {
if (centerComponent.isShowing()) {
centerComponent.requestFocus();
}
int[] toLeft = getPrimaryBegginings(myDividers[0].getPaint());
int[] toRight = getPrimaryBegginings(myDividers[1].getPaint());
int[] toLeft = getPrimaryBeginnings(myDividers[0].getPaint());
int[] toRight = getPrimaryBeginnings(myDividers[1].getPaint());
int line;
if (toLeft.length > 0 && toRight.length > 0) {
line = Math.min(toLeft[0], toRight[0]);
@@ -417,10 +411,10 @@ public class MergePanel2 implements DiffViewer {
SyncScrollSupport.scrollEditor(centerEditor, line);
}
private int[] getPrimaryBegginings(DiffDividerPaint paint) {
private int[] getPrimaryBeginnings(DiffDividerPaint paint) {
FragmentSide primarySide = paint.getLeftSide();
LOG.assertTrue(getEditor(1) == paint.getSides().getEditor(primarySide));
return paint.getSides().getLineBlocks().getBegginings(primarySide);
return paint.getSides().getLineBlocks().getBeginnings(primarySide, true);
}
}
@@ -466,6 +460,7 @@ public class MergePanel2 implements DiffViewer {
return getContentType();
}
@Nullable
public Project getProject() {
return myData == null ? null : myData.getProject();
}
@@ -512,12 +507,13 @@ public class MergePanel2 implements DiffViewer {
mySide = side;
}
@Nullable
public Editor getEditor() {
return MergePanel2.this.getEditor(mySide.getMergeIndex());
}
public int[] getFragmentStartingLines() {
return myMergeList.getChanges(mySide).getLineBlocks().getBegginings(MergeList.BRANCH_SIDE);
return myMergeList.getChanges(mySide).getLineBlocks().getBeginnings(MergeList.BRANCH_SIDE);
}
}
@@ -531,7 +527,7 @@ public class MergePanel2 implements DiffViewer {
if (myMergeList != null) {
for (int i = 0; i < 2; i++) {
FragmentSide branchSide = FragmentSide.fromIndex(i);
beginnings.addAll(myMergeList.getChanges(branchSide).getLineBlocks().getBegginings(MergeList.BASE_SIDE));
beginnings.addAll(myMergeList.getChanges(branchSide).getLineBlocks().getBeginnings(MergeList.BASE_SIDE));
}
}
int[] result = beginnings.toArray();
@@ -540,6 +536,7 @@ public class MergePanel2 implements DiffViewer {
}
}
@Nullable
public static MergePanel2 fromDataContext(DataContext dataContext) {
DiffViewer diffComponent = PlatformDataKeys.DIFF_VIEWER.getData(dataContext);
return diffComponent instanceof MergePanel2 ? (MergePanel2)diffComponent : null;
@@ -22,20 +22,18 @@ import com.intellij.openapi.diff.impl.highlighting.FragmentSide;
import com.intellij.openapi.diff.impl.incrementalMerge.Change;
import com.intellij.openapi.diff.impl.incrementalMerge.ChangeList;
import com.intellij.openapi.diff.impl.util.TextDiffType;
import com.intellij.util.containers.IntArrayList;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class LineBlocks {
public static final LineBlocks EMPTY = new LineBlocks(SimpleIntervalProvider.EMPTY, new TextDiffType[0]);
public static final LineBlocks EMPTY = new LineBlocks(Collections.<Diff>emptyList());
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.splitter.LineBlocks");
private final IntervalsProvider myIntervalsProvider;
private final TextDiffType[] myTypes;
private final List<Diff> myDiffs;
private LineBlocks(IntervalsProvider intervalsProvider, TextDiffType[] types) {
myIntervalsProvider = intervalsProvider;
myTypes = types;
private LineBlocks(List<Diff> diffs) {
myDiffs = diffs;
}
public Interval getVisibleIndices(Trapezium visibleArea) {
@@ -56,23 +54,40 @@ public class LineBlocks {
}
public TextDiffType getType(int index) {
return myTypes[index];
return myDiffs.get(index).getDiffType();
}
public int getCount() {
return getBegginings(FragmentSide.SIDE1).length;
return getBeginnings(FragmentSide.SIDE1).length;
}
public int[] getBegginings(FragmentSide side) {
IntArrayList result = new IntArrayList(getIntervals(side).length);
/**
* Get beginnings of all the changes from the specified side.
* @param side Side of the changes.
*/
public int[] getBeginnings(FragmentSide side) {
return getBeginnings(side, false);
}
/**
* Get beginnings of the changes from the specified side.
* @param side Side of the changes.
* @param unappliedOnly If true - only unapplied changes will be considered, if false - all changes (both applied and not applied).
*/
public int[] getBeginnings(FragmentSide side, boolean unappliedOnly) {
List<Integer> result = new ArrayList<Integer>(myDiffs.size());
int previousBeginning = Integer.MIN_VALUE;
Interval[] sideIntervals = getIntervals(side);
for (Interval sideInterval : sideIntervals) {
int start = sideInterval.getStart();
if (start != previousBeginning) result.add(start);
previousBeginning = start;
for (Diff diff : myDiffs) {
if (!unappliedOnly || !diff.getDiffType().isApplied()) {
Interval interval = diff.getInterval(side);
int start = interval.getStart();
if (start != previousBeginning) result.add(start);
previousBeginning = start;
}
}
return result.toArray();
return ArrayUtil.toIntArray(result);
}
static int getMaxStartedIndex(Interval[] intervals, int start) {
@@ -99,7 +114,19 @@ public class LineBlocks {
}
public Interval[] getIntervals(FragmentSide side) {
return myIntervalsProvider.getIntervals(side);
Interval[] intervals = new Interval[myDiffs.size()];
for (int i = 0; i < intervals.length; i++) {
intervals[i] = myDiffs.get(i).getInterval(side);
}
return intervals;
}
public TextDiffType[] getTypes() {
TextDiffType[] types = new TextDiffType[myDiffs.size()];
for (int i = 0; i < types.length; i++) {
types[i] = myDiffs.get(i).getDiffType();
}
return types;
}
private int transform(int location, Interval[] domain, Interval[] range) {
@@ -147,20 +174,13 @@ public class LineBlocks {
static LineBlocks createLineBlocks(LineBlock[] blocks) {
Arrays.sort(blocks, LineBlock.COMPARATOR);
Interval[] intervals1 = new Interval[blocks.length];
Interval[] intervals2 = new Interval[blocks.length];
TextDiffType[] types = new TextDiffType[blocks.length];
for (int i = 0; i < blocks.length; i++) {
LineBlock block = blocks[i];
intervals1[i] = new Interval(block.getStartingLine1(), block.getModifiedLines1());
intervals2[i] = new Interval(block.getStartingLine2(), block.getModifiedLines2());
types[i] = TextDiffType.create(block.getType());
List<Diff> diffs = new ArrayList<Diff>(blocks.length);
for (LineBlock block : blocks) {
Interval interval1 = new Interval(block.getStartingLine1(), block.getModifiedLines1());
Interval interval2 = new Interval(block.getStartingLine2(), block.getModifiedLines2());
diffs.add(new Diff(interval1, interval2, TextDiffType.create(block.getType())));
}
return create(intervals1, intervals2, types);
}
private static LineBlocks create(Interval[] intervals1, Interval[] intervals2, TextDiffType[] types) {
return new LineBlocks(new SimpleIntervalProvider(intervals1, intervals2), types);
return new LineBlocks(diffs);
}
@NotNull
@@ -168,44 +188,41 @@ public class LineBlocks {
// changes may come mixed, need to sort them to get correct intervals
Collections.sort(changes, ChangeList.CHANGE_ORDER);
ArrayList<Interval> intervals1 = new ArrayList<Interval>();
ArrayList<Interval> intervals2 = new ArrayList<Interval>();
ArrayList<TextDiffType> types = new ArrayList<TextDiffType>();
List<Diff> diffs = new ArrayList<Diff>(changes.size());
for (Change change : changes) {
if (!change.isValid()) { continue; }
int start1 = change.getChangeSide(FragmentSide.SIDE1).getStartLine();
int end1 = change.getChangeSide(FragmentSide.SIDE1).getEndLine();
intervals1.add(Interval.fromTo(start1, end1));
Interval interval1 = Interval.fromTo(start1, end1);
int start2 = change.getChangeSide(FragmentSide.SIDE2).getStartLine();
int end2 = change.getChangeSide(FragmentSide.SIDE2).getEndLine();
intervals2.add(Interval.fromTo(start2, end2));
Interval interval2 = Interval.fromTo(start2, end2);
types.add(change.getType().getTypeKey());
diffs.add(new Diff(interval1, interval2, change.getType().getTypeKey()));
}
return create(intervals1.toArray(new Interval[intervals1.size()]),
intervals2.toArray(new Interval[intervals2.size()]), types.toArray(new TextDiffType[types.size()]));
return new LineBlocks(diffs);
}
public TextDiffType[] getTypes() {
return myTypes;
}
private static class Diff {
public interface IntervalsProvider {
Interval[] getIntervals(FragmentSide side);
}
public static class SimpleIntervalProvider implements IntervalsProvider {
private final Interval[][] myIntervals = new Interval[2][];
public static final IntervalsProvider EMPTY = new SimpleIntervalProvider(new Interval[0], new Interval[0]);
@NotNull private final Interval myIntervalForSide1;
@NotNull private final Interval myIntervalForSide2;
@NotNull private final TextDiffType myDiffType;
public SimpleIntervalProvider(Interval[] intervals1, Interval[] intervals2) {
myIntervals[0] = intervals1;
myIntervals[1] = intervals2;
private Diff(@NotNull Interval intervalForSide1, @NotNull Interval intervalForSide2, @NotNull TextDiffType type) {
myIntervalForSide1 = intervalForSide1;
myIntervalForSide2 = intervalForSide2;
myDiffType = type;
}
public Interval[] getIntervals(FragmentSide side) {
return myIntervals[side.getIndex()];
@NotNull
public TextDiffType getDiffType() {
return myDiffType;
}
public Interval getInterval(FragmentSide side) {
return side == FragmentSide.SIDE1 ? myIntervalForSide1 : myIntervalForSide2;
}
}
}
@@ -17,11 +17,13 @@ package com.intellij.openapi.diff.impl.util;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.editor.Editor;
import org.jetbrains.annotations.Nullable;
public interface FocusDiffSide {
DataKey<FocusDiffSide> DATA_KEY = DataKey.create("focusedDiffSide");
@Deprecated String FOCUSED_DIFF_SIDE = DATA_KEY.getName();
@Nullable
Editor getEditor();
int[] getFragmentStartingLines();
@@ -382,6 +382,6 @@ public class TempFileSystem extends NewVirtualFileSystem {
final FSItem item = convert(file);
if (item == null) return null;
final long length = item instanceof FSFile ? ((FSFile)item).myContent.length : 0;
return new FileAttributes(item.isDirectory(), false, false, length, item.myTimestamp, item.myWritable);
return new FileAttributes(item.isDirectory(), false, false, false, length, item.myTimestamp, item.myWritable);
}
}
@@ -20,7 +20,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileAttributes;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.impl.local.LocalFileSystemBase;
import com.intellij.util.ArrayUtil;
@@ -222,9 +221,10 @@ public class Win32LocalFileSystem extends LocalFileSystemBase {
if (fileInfo == null) return null;
final boolean isDirectory = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_DIRECTORY);
final boolean isSymlink = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_REPARSE_POINT);
final boolean isSpecial = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_DEVICE);
final boolean isSymlink = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_REPARSE_POINT);
final boolean isHidden = isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_HIDDEN);
final boolean isWritable = !isSet(fileInfo.attributes, Win32Kernel.FILE_ATTRIBUTE_READONLY);
return new FileAttributes(isDirectory, isSymlink, isSpecial, fileInfo.length, fileInfo.timestamp, isWritable);
return new FileAttributes(isDirectory, isSpecial, isSymlink, isHidden, fileInfo.length, fileInfo.timestamp, isWritable);
}
}
@@ -114,6 +114,7 @@ public class FileWatcherTest extends PlatformLangTestCase {
try {
myConnection.disconnect();
myWatcher.shutdown();
assertFalse(myWatcher.isOperational());
}
finally {
myFileSystem = null;
@@ -15,13 +15,10 @@
*/
package com.intellij.openapi.vfs.local;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
@@ -30,6 +27,8 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import static com.intellij.openapi.util.io.IoTestUtil.createTempLink;
public class SymlinkHandlingTest extends LightPlatformLangTestCase {
private LocalFileSystem myFileSystem;
@@ -265,32 +264,6 @@ public class SymlinkHandlingTest extends LightPlatformLangTestCase {
assertEquals(vSubChild, vSubChildRel);
}
// todo[r.sh] use NIO2 API after migration to JDK 7
public static File createTempLink(final String target, final String link) throws InterruptedException, ExecutionException {
final boolean isAbsolute = SystemInfo.isUnix && StringUtil.startsWithChar(link, '/') ||
SystemInfo.isWindows && link.matches("^[c-zC-Z]:[/\\\\].*$");
final File linkFile = isAbsolute ? new File(link) : new File(FileUtil.getTempDirectory(), link);
assertTrue(link, !linkFile.exists() || linkFile.delete());
final File parentDir = linkFile.getParentFile();
assertTrue("link=" + link + ", parent=" + parentDir, parentDir != null && (parentDir.isDirectory() || parentDir.mkdirs()));
final GeneralCommandLine commandLine;
if (SystemInfo.isWindows) {
commandLine = new File(target).isDirectory()
? new GeneralCommandLine("cmd", "/C", "mklink", "/D", linkFile.getAbsolutePath(), target)
: new GeneralCommandLine("cmd", "/C", "mklink", linkFile.getAbsolutePath(), target);
}
else {
commandLine = new GeneralCommandLine("ln", "-s", target, linkFile.getAbsolutePath());
}
final int res = commandLine.createProcess().waitFor();
assertEquals(commandLine.getCommandLineString(), 0, res);
final File targetFile = new File(target);
assertEquals("target=" + target + ", link=" + linkFile, targetFile.exists(), linkFile.exists());
return linkFile;
}
@Nullable
private VirtualFile refreshAndFind(final File ioFile) {
refresh();
@@ -25,6 +25,7 @@ import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.psi.tree.IElementType;
import junit.framework.Assert;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@@ -35,6 +36,10 @@ import java.util.List;
* Time: 21:00:49
*/
public class EditorTestUtil {
public static final String CARET_TAG = "<caret>";
public static final String SELECTION_START_TAG = "<selection>";
public static final String SELECTION_END_TAG = "</selection>";
public static final char BACKSPACE_FAKE_CHAR = '\uFFFF';
public static final char SMART_ENTER_FAKE_CHAR = '\uFFFE';
@@ -83,4 +88,33 @@ public class EditorTestUtil {
}
return tokens;
}
public static int getCaretPosition(@NotNull final String content) {
return getCaretAndSelectionPosition(content)[0];
}
public static int[] getCaretAndSelectionPosition(@NotNull final String content) {
int caretPosInSourceFile = content.indexOf(CARET_TAG);
int selectionStartInSourceFile = content.indexOf(SELECTION_START_TAG);
int selectionEndInSourceFile = content.indexOf(SELECTION_END_TAG);
if (selectionStartInSourceFile >= 0) {
if (caretPosInSourceFile >= 0) {
if (caretPosInSourceFile < selectionStartInSourceFile) {
selectionStartInSourceFile -= CARET_TAG.length();
selectionEndInSourceFile -= CARET_TAG.length();
}
else {
if (caretPosInSourceFile < selectionEndInSourceFile) {
caretPosInSourceFile -= SELECTION_START_TAG.length();
}
else {
caretPosInSourceFile -= SELECTION_START_TAG.length() + SELECTION_END_TAG.length();
}
}
}
selectionEndInSourceFile -= SELECTION_START_TAG.length();
}
return new int[]{caretPosInSourceFile, selectionStartInSourceFile, selectionEndInSourceFile};
}
}
@@ -93,7 +93,8 @@ public abstract class LexerTestCase extends UsefulTestCase {
String fileName = PathManager.getHomePath() + "/" + getDirPath() + "/" + getTestName(true) + "." + fileExt;
String text = "";
try {
text = StringUtil.convertLineSeparators(FileUtil.loadFile(new File(fileName)).trim());
String fileText = FileUtil.loadFile(new File(fileName));
text = StringUtil.convertLineSeparators(shouldTrim() ? fileText.trim() : fileText);
}
catch (IOException e) {
fail("can't load file " + fileName + ": " + e.getMessage());
@@ -101,6 +102,10 @@ public abstract class LexerTestCase extends UsefulTestCase {
doTest(text);
}
protected boolean shouldTrim() {
return true;
}
private static String getTokenText(Lexer lexer) {
String text = lexer.getBufferSequence().subSequence(lexer.getTokenStart(), lexer.getTokenEnd()).toString();
text = StringUtil.replace(text, "\n", "\\n");
@@ -26,6 +26,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.PsiManager;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl;
import org.jetbrains.annotations.NonNls;
@@ -77,15 +78,20 @@ public abstract class LightPlatformCodeInsightFixtureTestCase extends UsefulTest
return null;
}
/**
* Return absolute path to the test data.
*
* @return absolute path to the test data.
*/
@NonNls
protected String getTestDataPath() {
return PathManager.getHomePath().replace(File.separatorChar, '/') + getBasePath();
}
/**
* Return absolute path to the test data. Not intended to be overridden.
*
* @return absolute path to the test data.
*/
@NonNls
protected String getTestDataPath() {
String path = isCommunity() ? PlatformTestUtil.getCommunityPath() : PathManager.getHomePath();
return path.replace(File.separatorChar, '/') + getBasePath();
}
protected boolean isCommunity() {
return false;
}
@Override
protected void runTest() throws Throwable {
@@ -58,7 +58,7 @@ public class LineBlock {
return myStartingLine2 + myModifiedLines2;
}
public static final Comparator COMPARATOR = new Comparator<LineBlock>() {
public static final Comparator<LineBlock> COMPARATOR = new Comparator<LineBlock>() {
public int compare(LineBlock block1, LineBlock block2) {
return block1.getStartingLine1() - block2.getStartingLine1();
}
@@ -18,12 +18,23 @@ package com.intellij.openapi.util.io;
import com.intellij.util.StringBuilderSpinAllocator;
import org.intellij.lang.annotations.MagicConstant;
import static com.intellij.util.BitUtil.isSet;
import static com.intellij.util.BitUtil.notSet;
/**
* @version 11.1
* @see FileSystemUtil#getAttributes(String)
*/
@SuppressWarnings("OctalInteger")
public final class FileAttributes {
public static final int DIRECTORY = 0x0001;
public static final int SPECIAL = 0x0002;
public static final int SYM_LINK = 0x0010;
public static final int HIDDEN = 0x0100;
@MagicConstant(flags = {DIRECTORY, SPECIAL, SYM_LINK, HIDDEN})
public @interface FileType { }
public static final int OWNER_READ = 0400;
public static final int OWNER_WRITE = 0200;
public static final int OWNER_EXECUTE = 0100;
@@ -34,52 +45,99 @@ public final class FileAttributes {
public static final int OTHERS_WRITE = 0002;
public static final int OTHERS_EXECUTE = 0001;
@MagicConstant(flags = {
OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, OTHERS_READ, OTHERS_WRITE, OTHERS_EXECUTE
})
@MagicConstant(flags = {OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, OTHERS_READ, OTHERS_WRITE, OTHERS_EXECUTE})
public @interface Permissions { }
public final boolean isFile;
public final boolean isDirectory;
public final boolean isSymlink;
public final boolean isSpecial;
/** In bytes, 0 for symlinks and special files. */
/**
* {@linkplain #DIRECTORY} and {@linkplain #SPECIAL} bits are mutually exclusive, none of them set means a regular file.<br/>
* {@linkplain #SYM_LINK} bit may be set along with above ones (which then denote a type of a link target).<br/>
* {@linkplain #HIDDEN} bit may be only set on Windows.
*/
@FileType
public final int type;
/**
* In bytes, 0 for special files.<br/>
* For symlinks - length of a link target.
*/
public final long length;
/** In milliseconds (note that actual resolution may be less accurate). */
/**
* In milliseconds (actual resolution depends on a file system and may be less accurate).<br/>
* For symlinks - timestamp of a link target.
*/
public final long lastModified;
/** UNIX permission bits (for Windows only OWNER_WRITE matters and OWNER_READ/EXECUTE are always set), or -1 if not supported. */
/**
* UNIX permission bits (for Windows only OWNER_WRITE matters and OWNER_READ/EXECUTE are always set), or -1 if not supported.<br/>
* For symlinks - permissions of a link target.
*/
@Permissions
public final int permissions;
// todo: hidden flag (?)
public FileAttributes(final boolean isDirectory,
final boolean isSymlink,
final boolean isSpecial,
final boolean isSymlink,
final boolean isHidden,
final long length,
final long lastModified,
final boolean writable) {
this(isDirectory, isSymlink, isSpecial, length, lastModified, OWNER_READ | OWNER_EXECUTE | (writable ? OWNER_WRITE : 0));
final boolean isWritable) {
this(type(isDirectory, isSpecial, isSymlink, isHidden), length, lastModified, OWNER_READ | OWNER_EXECUTE | (isWritable ? OWNER_WRITE : 0));
}
public FileAttributes(final boolean isDirectory,
final boolean isSymlink,
final boolean isSpecial,
final boolean isSymlink,
final long length,
final long lastModified,
@Permissions final int permissions) {
this.isFile = !isDirectory && !isSymlink && !isSpecial;
this.isDirectory = isDirectory;
this.isSymlink = isSymlink;
this.isSpecial = isSpecial;
this.length = isSymlink || isSpecial ? 0 : length;
this(type(isDirectory, isSpecial, isSymlink, false), length, lastModified, permissions);
}
private FileAttributes(@FileType final int type,
final long length,
final long lastModified,
@Permissions final int permissions) {
if (isSet(type, DIRECTORY) && isSet(type, SPECIAL)) {
throw new IllegalArgumentException("DIRECTORY and SPECIAL bits cannot be set simultaneously");
}
this.type = type;
this.length = length;
this.lastModified = lastModified;
this.permissions = permissions;
}
@FileType
private static int type(final boolean isDirectory, final boolean isSpecial, final boolean isSymlink, final boolean isHidden) {
@FileType int type = 0;
if (isDirectory) type |= DIRECTORY;
if (isSpecial) type |= SPECIAL;
if (isSymlink) type |= SYM_LINK;
if (isHidden) type |= HIDDEN;
return type;
}
/** Is {@code true} for files and symlinks to files (see {@linkplain #isRegularFile()}). */
public boolean isFile() {
return notSet(type, DIRECTORY | SPECIAL);
}
/** Is {@code true} for pure regular files only (see {@linkplain #isFile()}). */
public boolean isRegularFile() {
return notSet(type, DIRECTORY | SPECIAL | SYM_LINK);
}
public boolean isSymLink() {
return isSet(type, SYM_LINK);
}
public boolean isWritable() {
return permissions == -1 || isSet(permissions, OWNER_WRITE) || isSet(permissions, GROUP_WRITE) || isSet(permissions, OTHERS_WRITE);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -87,10 +145,7 @@ public final class FileAttributes {
final FileAttributes that = (FileAttributes)o;
if (isDirectory != that.isDirectory) return false;
if (isFile != that.isFile) return false;
if (isSpecial != that.isSpecial) return false;
if (isSymlink != that.isSymlink) return false;
if (type != that.type) return false;
if (lastModified != that.lastModified) return false;
if (permissions != that.permissions) return false;
if (length != that.length) return false;
@@ -100,10 +155,7 @@ public final class FileAttributes {
@Override
public int hashCode() {
int result = (isFile ? 1 : 0);
result = 31 * result + (isDirectory ? 1 : 0);
result = 31 * result + (isSymlink ? 1 : 0);
result = 31 * result + (isSpecial ? 1 : 0);
int result = type;
result = 31 * result + (int)(length ^ (length >>> 32));
result = 31 * result + (int)(lastModified ^ (lastModified >>> 32));
result = 31 * result + permissions;
@@ -115,10 +167,10 @@ public final class FileAttributes {
final StringBuilder sb = StringBuilderSpinAllocator.alloc();
try {
sb.append("[type:");
if (isFile) sb.append('f');
if (isDirectory) sb.append('d');
if (isSymlink) sb.append('l');
if (isSpecial) sb.append('!');
if (isSet(type, DIRECTORY)) sb.append('d');
else if (isSet(type, SPECIAL)) sb.append('!');
else sb.append('f');
if (isSet(type, SYM_LINK)) sb.append('l');
sb.append(" length:").append(length);
@@ -18,12 +18,14 @@ package com.intellij.openapi.util.io;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.ArrayUtil;
import com.intellij.util.SystemProperties;
import com.sun.jna.Library;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.io.File;
import java.lang.reflect.Array;
@@ -31,47 +33,73 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import static com.intellij.util.BitUtil.isSet;
import static com.intellij.util.BitUtil.notSet;
/**
* @version 11.1
*/
public class FileSystemUtil {
public static final String FORCE_USE_NIO2_KEY = "idea.io.use.nio2";
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.io.FileSystemUtil");
private interface Mediator {
@Nullable
FileAttributes getAttributes(@NotNull String path) throws Exception;
@Nullable
String resolveSymLink(@NotNull String path) throws Exception;
void setPermissions(@NotNull String path, int permissions) throws Exception;
@NotNull
String getName();
}
@NotNull
private static final Mediator ourMediator;
static {
Mediator mediator = null;
private static Mediator ourMediator = getMediator();
// todo: move IdeaWin32 interface to this package, add mediator
private static Mediator getMediator() {
final boolean forceUseNio2 = SystemProperties.getBooleanProperty(FORCE_USE_NIO2_KEY, false);
final String quickTestPath = SystemInfo.isWindows ? "C:\\" : "/";
if (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris || SystemInfo.isFreeBSD) {
if (!forceUseNio2) {
if (SystemInfo.isLinux || SystemInfo.isMac || SystemInfo.isSolaris || SystemInfo.isFreeBSD) {
try {
final Mediator mediator = new JnaUnixMediatorImpl();
mediator.getAttributes(quickTestPath);
return mediator;
}
catch (Throwable t) {
LOG.warn(t);
}
}
}
if (SystemInfo.isJavaVersionAtLeast("1.7") && !"1.7.0-ea".equals(SystemInfo.JAVA_VERSION)) {
try {
mediator = new JnaUnixMediatorImpl();
mediator.getAttributes("/"); // quick test
final Mediator mediator = new Nio2MediatorImpl();
mediator.getAttributes(quickTestPath);
return mediator;
}
catch (Throwable t) {
LOG.error(t);
mediator = null;
LOG.warn(t);
}
}
if (mediator == null && SystemInfo.isJavaVersionAtLeast("1.7") && !"1.7.0-ea".equals(SystemInfo.JAVA_VERSION)) {
try {
mediator = new Jdk7MediatorImpl();
mediator.getAttributes("/"); // quick test
}
catch (Throwable t) {
LOG.error(t);
mediator = null;
}
}
// todo: after introducing IdeaWin32 mediator, fail tests at this point, or issue a warning in production
return new StandardMediatorImpl();
}
if (mediator == null) {
// todo: after introducing IdeaWin32 mediator, fail tests at this point, or issue a warning in production
mediator = new StandardMediatorImpl();
}
@TestOnly
static void resetMediator() {
ourMediator = getMediator();
}
ourMediator = mediator;
@TestOnly
static String getMediatorName() {
return ourMediator.getName();
}
private FileSystemUtil() { }
@@ -87,14 +115,21 @@ public class FileSystemUtil {
return null;
}
@SuppressWarnings("UnusedDeclaration")
@Nullable
public static FileAttributes getAttributes(@NotNull final File file) {
return getAttributes(file.getAbsolutePath());
}
public static boolean isSymLink(@NotNull final String path) {
if (SystemInfo.areSymLinksSupported) {
final FileAttributes attributes = getAttributes(path);
return attributes != null && attributes.isSymlink;
return attributes != null && attributes.isSymLink();
}
return false;
}
@SuppressWarnings("UnusedDeclaration")
public static boolean isSymLink(@NotNull final File file) {
return isSymLink(file.getAbsolutePath());
}
@@ -113,6 +148,7 @@ public class FileSystemUtil {
return null;
}
@SuppressWarnings("UnusedDeclaration")
@Nullable
public static String resolveSymLink(@NotNull final File file) {
return resolveSymLink(file.getAbsolutePath());
@@ -123,6 +159,7 @@ public class FileSystemUtil {
return attributes != null ? attributes.permissions : -1;
}
@SuppressWarnings("UnusedDeclaration")
public static int getPermissions(@NotNull final File file) {
return getPermissions(file.getAbsolutePath());
}
@@ -142,19 +179,10 @@ public class FileSystemUtil {
setPermissions(file.getAbsolutePath(), permissions);
}
private interface Mediator {
@Nullable
FileAttributes getAttributes(@NotNull String path) throws Exception;
@Nullable
String resolveSymLink(@NotNull String path) throws Exception;
void setPermissions(@NotNull String path, int permissions) throws Exception;
}
// todo[r.sh] remove reflection after migration to JDK 7
@SuppressWarnings("OctalInteger")
private static class Jdk7MediatorImpl implements Mediator {
private static class Nio2MediatorImpl implements Mediator {
private final Object myDefaultFileSystem;
private final Method myGetPath;
private final Method myIsSymbolicLink;
@@ -163,8 +191,9 @@ public class FileSystemUtil {
private final Method myReadAttributes;
private final Method mySetAttribute;
private final Method myToMillis;
private final String mySchema;
private Jdk7MediatorImpl() throws Exception {
private Nio2MediatorImpl() throws Exception {
myDefaultFileSystem = Class.forName("java.nio.file.FileSystems").getMethod("getDefault").invoke(null);
myGetPath = Class.forName("java.nio.file.FileSystem").getMethod("getPath", String.class, String[].class);
@@ -189,29 +218,33 @@ public class FileSystemUtil {
final Class<?> fileTimeClass = Class.forName("java.nio.file.attribute.FileTime");
myToMillis = fileTimeClass.getMethod("toMillis");
myToMillis.setAccessible(true);
mySchema = SystemInfo.isWindows ? "dos:*" : "posix:*";
}
@Override
public FileAttributes getAttributes(@NotNull final String path) throws Exception {
try {
final Object pathObj = myGetPath.invoke(myDefaultFileSystem, path, ArrayUtil.EMPTY_STRING_ARRAY);
Map attributes = (Map)myReadAttributes.invoke(null, pathObj, mySchema, myNoFollowLinkOptions);
final Boolean isSymbolicLink = (Boolean)attributes.get("isSymbolicLink");
if (isSymbolicLink) {
attributes = (Map)myReadAttributes.invoke(null, pathObj, mySchema, myLinkOptions);
}
final boolean isDirectory = (Boolean)attributes.get("isDirectory");
final boolean isOther = (Boolean)attributes.get("isOther");
final long size = (Long)attributes.get("size");
final long lastModified = (Long)myToMillis.invoke(attributes.get("lastModifiedTime"));
if (SystemInfo.isWindows) {
final Map attributes = (Map)myReadAttributes.invoke(null, pathObj, "dos:*", myNoFollowLinkOptions);
return new FileAttributes((Boolean)attributes.get("isDirectory"),
(Boolean)attributes.get("isSymbolicLink"),
(Boolean)attributes.get("isOther"),
(Long)attributes.get("size"),
(Long)myToMillis.invoke(attributes.get("lastModifiedTime")),
!(Boolean)attributes.get("readonly"));
final boolean isHidden = (Boolean)attributes.get("hidden");
final boolean isWritable = !(Boolean)attributes.get("readonly");
return new FileAttributes(isDirectory, isOther, isSymbolicLink, isHidden, size, lastModified, isWritable);
}
else {
final Map attributes = (Map)myReadAttributes.invoke(null, pathObj, "posix:*", myNoFollowLinkOptions);
return new FileAttributes((Boolean)attributes.get("isDirectory"),
(Boolean)attributes.get("isSymbolicLink"),
(Boolean)attributes.get("isOther"),
(Long)attributes.get("size"),
(Long)myToMillis.invoke(attributes.get("lastModifiedTime")),
decodePermissions(attributes.get("permissions")));
final int permissions = decodePermissions(attributes.get("permissions"));
return new FileAttributes(isDirectory, isOther, isSymbolicLink, size, lastModified, permissions);
}
}
catch (InvocationTargetException e) {
@@ -243,6 +276,12 @@ public class FileSystemUtil {
}
}
@NotNull
@Override
public String getName() {
return "NIO2";
}
private static final Map<String, Integer> ATTRIBUTES_MAP;
static {
ATTRIBUTES_MAP = new HashMap<String, Integer>();
@@ -290,6 +329,7 @@ public class FileSystemUtil {
}
}
// thanks to SVNKit for the idea
private static class JnaUnixMediatorImpl implements Mediator {
@SuppressWarnings("OctalInteger")
@@ -302,7 +342,9 @@ public class FileSystemUtil {
int PERM_MASK = 0777;
int lstat(String path, Pointer stat);
int stat(String path, Pointer stat);
int __lxstat64(int ver, String path, Pointer stat);
int __xstat64(int ver, String path, Pointer stat);
int chmod(String path, int mode);
}
@@ -333,20 +375,26 @@ public class FileSystemUtil {
@Override
public synchronized FileAttributes getAttributes(@NotNull final String path) throws Exception {
mySharedMem.clear();
final int res = SystemInfo.isLinux ? myLibC.__lxstat64(0, path, mySharedMem) : myLibC.lstat(path, mySharedMem);
if (res == 0) {
final int mode = (SystemInfo.isLinux ? mySharedMem.getInt(myModeOffset) : mySharedMem.getShort(myModeOffset)) & LibC.S_MASK;
final boolean isDirectory = (mode & LibC.S_IFDIR) == LibC.S_IFDIR;
final boolean isSymlink = (mode & LibC.S_IFLNK) == LibC.S_IFLNK;
final boolean isSpecial = !isDirectory && !isSymlink && (mode & LibC.S_IFREG) == 0;
final long size = mySharedMem.getLong(mySizeOffset);
final long mTime1 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset) : mySharedMem.getLong(myTimeOffset);
final long mTime2 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset + 4) : mySharedMem.getLong(myTimeOffset + 8);
final long mTime = mTime1 * 1000 + mTime2 / 1000000;
@FileAttributes.Permissions final int permissions = mode & LibC.PERM_MASK;
return new FileAttributes(isDirectory, isSymlink, isSpecial, size, mTime, permissions);
int res = SystemInfo.isLinux ? myLibC.__lxstat64(0, path, mySharedMem) : myLibC.lstat(path, mySharedMem);
if (res != 0) return null;
int mode = (SystemInfo.isLinux ? mySharedMem.getInt(myModeOffset) : mySharedMem.getShort(myModeOffset)) & LibC.S_MASK;
final boolean isSymlink = (mode & LibC.S_IFLNK) == LibC.S_IFLNK;
if (isSymlink) {
mySharedMem.clear();
res = SystemInfo.isLinux ? myLibC.__xstat64(0, path, mySharedMem) : myLibC.stat(path, mySharedMem);
if (res != 0) return null;
mode = (SystemInfo.isLinux ? mySharedMem.getInt(myModeOffset) : mySharedMem.getShort(myModeOffset)) & LibC.S_MASK;
}
return null;
final boolean isDirectory = (mode & LibC.S_IFDIR) == LibC.S_IFDIR;
final boolean isSpecial = !isDirectory && (mode & LibC.S_IFREG) == 0;
final long size = mySharedMem.getLong(mySizeOffset);
final long mTime1 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset) : mySharedMem.getLong(myTimeOffset);
final long mTime2 = SystemInfo.is32Bit ? mySharedMem.getInt(myTimeOffset + 4) : mySharedMem.getLong(myTimeOffset + 8);
final long mTime = mTime1 * 1000 + mTime2 / 1000000;
@FileAttributes.Permissions final int permissions = mode & LibC.PERM_MASK;
return new FileAttributes(isDirectory, isSpecial, isSymlink, size, mTime, permissions);
}
@Override
@@ -358,12 +406,20 @@ public class FileSystemUtil {
public void setPermissions(@NotNull final String path, final int permissions) throws Exception {
myLibC.chmod(path, permissions & LibC.PERM_MASK);
}
@NotNull
@Override
public String getName() {
return "JnaUnix";
}
}
private static class StandardMediatorImpl implements Mediator {
// from java.io.FileSystem
private static final int BA_REGULAR = 0x02;
private static final int BA_DIRECTORY = 0x04;
private static final int BA_HIDDEN = 0x08;
private final Object myFileSystem;
private final Method myGetBooleanAttributes;
@@ -393,16 +449,18 @@ public class FileSystemUtil {
if (myFileSystem != null) {
final int flags = (Integer)myGetBooleanAttributes.invoke(myFileSystem, file);
if (flags != 0) {
final boolean isDirectory = (flags & BA_DIRECTORY) != 0;
final boolean isSpecial = (flags & (BA_REGULAR | BA_DIRECTORY)) == 0;
return new FileAttributes(isDirectory, false, isSpecial, file.length(), file.lastModified(), file.canWrite());
final boolean isDirectory = isSet(flags, BA_DIRECTORY);
final boolean isSpecial = notSet(flags, BA_REGULAR | BA_DIRECTORY);
final boolean isHidden = isSet(flags, BA_HIDDEN);
return new FileAttributes(isDirectory, isSpecial, false, isHidden, file.length(), file.lastModified(), file.canWrite());
}
}
else {
if (file.exists()) {
final boolean isDirectory = file.isDirectory();
final boolean isSpecial = !isDirectory && !file.isFile();
return new FileAttributes(isDirectory, false, isSpecial, file.length(), file.lastModified(), file.canWrite());
final boolean isHidden = file.isHidden();
return new FileAttributes(isDirectory, isSpecial, false, isHidden, file.length(), file.lastModified(), file.canWrite());
}
}
@@ -417,5 +475,11 @@ public class FileSystemUtil {
@Override
public void setPermissions(@NotNull final String path, final int permissions) throws Exception {
}
@NotNull
@Override
public String getName() {
return "fallback";
}
}
}
@@ -1953,6 +1953,22 @@ public class StringUtil extends StringUtilRt {
return res;
}
public static int getOccurrenceCount(@NotNull String text, final String s) {
int res = 0;
int i = 0;
while (i < text.length()) {
i = text.indexOf(s, i);
if (i >= 0) {
res++;
i++;
}
else {
break;
}
}
return res;
}
@NotNull
public static String fixVariableNameDerivedFromPropertyName(@NotNull String name) {
char c = name.charAt(0);
@@ -19,7 +19,11 @@ package com.intellij.util;
* @since 27.03.2012
*/
public class BitUtil {
public static boolean isSet(final int mask, final int flag) {
return (mask & flag) == flag;
public static boolean isSet(final int value, final int flag) {
return (value & flag) == flag;
}
public static boolean notSet(final int value, final int mask) {
return (value & mask) == 0;
}
}
@@ -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.openapi.util.io;
import com.intellij.openapi.util.SystemInfo;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
public class FileAttributesNio2ReadingTest extends FileAttributesReadingTest {
@BeforeClass
public static void setUpClass() throws Exception {
System.setProperty(FileSystemUtil.FORCE_USE_NIO2_KEY, "true");
FileSystemUtil.resetMediator();
assertEquals("NIO2", FileSystemUtil.getMediatorName());
}
@AfterClass
public static void tearDownClass() throws Exception {
System.setProperty(FileSystemUtil.FORCE_USE_NIO2_KEY, "");
FileSystemUtil.resetMediator();
}
@Override
@Before
public void setUp() throws Exception {
assumeTrue(SystemInfo.isJavaVersionAtLeast("1.7"));
super.setUp();
}
}
@@ -0,0 +1,154 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.util.io;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
public class FileAttributesReadingTest {
private final byte[] myTestData = new byte[]{'t', 'e', 's', 't'};
private File myTempDirectory;
@BeforeClass
public static void checkMediator() throws Exception {
final String expectedName = SystemInfo.isWindows ? "IdeaWin32" : "JnaUnix";
assertEquals(expectedName, FileSystemUtil.getMediatorName());
}
@Before
public void setUp() throws Exception {
myTempDirectory = FileUtil.createTempDirectory(getClass().getName(), ".tmp");
}
@After
public void tearDown() throws Exception {
if (myTempDirectory != null) {
FileUtil.delete(myTempDirectory);
}
}
@Test
public void regularFile() throws Exception {
final File file = FileUtil.createTempFile(myTempDirectory, "test.", ".txt", true);
FileUtil.writeToFile(file, myTestData);
final FileAttributes attributes = getAttributes(file);
assertEquals(0, attributes.type);
assertEquals(myTestData.length, attributes.length);
assertTimestampEquals(file.lastModified(), attributes.lastModified);
assertTrue(attributes.isWritable());
}
@Test
public void directory() throws Exception {
final File file = FileUtil.createTempDirectory(myTempDirectory, "test.", ".tmp");
final FileAttributes attributes = getAttributes(file);
assertEquals(FileAttributes.DIRECTORY, attributes.type);
assertEquals(file.length(), attributes.length);
assertTimestampEquals(file.lastModified(), attributes.lastModified);
assertTrue(attributes.isWritable());
}
@Test
public void special() throws Exception {
assumeTrue(SystemInfo.isUnix);
final File file = new File("/dev/null");
final FileAttributes attributes = getAttributes(file);
assertEquals(FileAttributes.SPECIAL, attributes.type);
assertEquals(0, attributes.length);
assertTrue(attributes.isWritable());
}
@Test
public void linkToFile() throws Exception {
final File file = FileUtil.createTempFile(myTempDirectory, "test.", ".txt", true);
FileUtil.writeToFile(file, myTestData);
assertTrue(file.setLastModified(file.lastModified() - 5000));
assertTrue(file.setWritable(false, false));
final File link = IoTestUtil.createTempLink(file.getPath(), new File(myTempDirectory, "link").getPath());
final FileAttributes attributes = getAttributes(link);
assertEquals(FileAttributes.SYM_LINK, attributes.type);
assertEquals(myTestData.length, attributes.length);
assertTimestampEquals(file.lastModified(), attributes.lastModified);
assertFalse(attributes.isWritable());
final String target = FileSystemUtil.resolveSymLink(link);
assertEquals(file.getPath(), target);
}
@Test
public void doubleLink() throws Exception {
final File file = FileUtil.createTempFile(myTempDirectory, "test.", ".txt", true);
FileUtil.writeToFile(file, myTestData);
assertTrue(file.setLastModified(file.lastModified() - 5000));
assertTrue(file.setWritable(false, false));
final File link1 = IoTestUtil.createTempLink(file.getPath(), new File(myTempDirectory, "link1").getPath());
final File link2 = IoTestUtil.createTempLink(link1.getPath(), new File(myTempDirectory, "link2").getPath());
final FileAttributes attributes = getAttributes(link2);
assertEquals(FileAttributes.SYM_LINK, attributes.type);
assertEquals(myTestData.length, attributes.length);
assertTimestampEquals(file.lastModified(), attributes.lastModified);
assertFalse(attributes.isWritable());
final String target = FileSystemUtil.resolveSymLink(link2);
assertEquals(file.getPath(), target);
}
@Test
public void linkToDirectory() throws Exception {
final File file = FileUtil.createTempDirectory(myTempDirectory, "test.", ".tmp");
if (SystemInfo.isUnix) assertTrue(file.setWritable(false, false));
assertTrue(file.setLastModified(file.lastModified() - 5000));
final File link = IoTestUtil.createTempLink(file.getPath(), new File(myTempDirectory, "link").getPath());
final FileAttributes attributes = getAttributes(link);
assertEquals(FileAttributes.DIRECTORY | FileAttributes.SYM_LINK, attributes.type);
assertEquals(file.length(), attributes.length);
assertTimestampEquals(file.lastModified(), attributes.lastModified);
if (SystemInfo.isUnix) assertFalse(attributes.isWritable());
final String target = FileSystemUtil.resolveSymLink(link);
assertEquals(file.getPath(), target);
}
@NotNull
private static FileAttributes getAttributes(@NotNull final File file) {
final FileAttributes attributes = FileSystemUtil.getAttributes(file.getPath());
assertNotNull(attributes);
System.out.println(attributes);
return attributes;
}
private static void assertTimestampEquals(final long expected, final long actual) {
final long roundedExpected = (expected / 1000) * 1000;
final long roundedActual = (actual / 1000) * 1000;
assertEquals("expected: " + expected + ", actual: " + actual,
roundedExpected, roundedActual);
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.util.io;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import java.io.File;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class IoTestUtil {
// todo[r.sh] use NIO2 API after migration to JDK 7
public static File createTempLink(final String target, final String link) throws InterruptedException, IOException {
final boolean isAbsolute = SystemInfo.isUnix && StringUtil.startsWithChar(link, '/') ||
SystemInfo.isWindows && link.matches("^[c-zC-Z]:[/\\\\].*$");
final File linkFile = isAbsolute ? new File(link) : new File(FileUtil.getTempDirectory(), link);
assertTrue(link, !linkFile.exists() || linkFile.delete());
final File parentDir = linkFile.getParentFile();
assertTrue("link=" + link + ", parent=" + parentDir, parentDir != null && (parentDir.isDirectory() || parentDir.mkdirs()));
final ProcessBuilder commandLine;
if (SystemInfo.isWindows) {
commandLine = new File(target).isDirectory()
? new ProcessBuilder("cmd", "/C", "mklink", "/D", linkFile.getAbsolutePath(), target)
: new ProcessBuilder("cmd", "/C", "mklink", linkFile.getAbsolutePath(), target);
}
else {
commandLine = new ProcessBuilder("ln", "-s", target, linkFile.getAbsolutePath());
}
final int res = commandLine.start().waitFor();
assertEquals(commandLine.command().toString(), 0, res);
final File targetFile = new File(target);
assertEquals("target=" + target + ", link=" + linkFile, targetFile.exists(), linkFile.exists());
return linkFile;
}
}
@@ -79,6 +79,8 @@ import java.util.List;
* Time: 4:00 PM
*/
public class ChangesFragmentedDiffPanel implements Disposable {
private static final int SHORT_DIFF_DIVIDER_POLYGONS_OFFSET = 2;
private final JPanel myPanel;
private final Project myProject;
private final JComponent myParent;
@@ -346,7 +348,7 @@ public class ChangesFragmentedDiffPanel implements Disposable {
}
private DiffPanel createPanel(final boolean horizontal) {
final DiffPanel diffPanel = new DiffPanelImpl(null, myProject, false, horizontal){
final DiffPanel diffPanel = new DiffPanelImpl(null, myProject, false, horizontal, SHORT_DIFF_DIVIDER_POLYGONS_OFFSET){
@Override
protected DiffPanelState createDiffPanelState(@NotNull Disposable parentDisposable) {
return new FragmentedDiffPanelState(this, myProject, ! horizontal, parentDisposable);
@@ -136,7 +136,7 @@ public class VcsDirtyScopeVfsListener implements ApplicationComponent, BulkFileL
if (!file.isInLocalFileSystem()) { continue; }
dirtyFilesAndDirs.add(file, true);
} else if (event instanceof VFileMoveEvent || event instanceof VFilePropertyChangeEvent) {
dirtyFilesAndDirs.add(file, false);
dirtyFilesAndDirs.add(file, true);
}
}
// and notify VCSDirtyScopeManager
@@ -19,6 +19,7 @@ import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAnnotationMethod;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Nullable;
enum InitializerRequirement {
@@ -32,7 +33,7 @@ enum InitializerRequirement {
InitializerRequirement r = NONE_REQUIRED;
final PsiMethod[] methods = psiClass.getMethods();
for (PsiMethod method : methods) {
if (method instanceof PsiAnnotationMethod) {
if (PsiUtil.isAnnotationMethod(method)) {
final PsiAnnotationMethod annotationMethod = (PsiAnnotationMethod)method;
if (annotationMethod.getDefaultValue() == null) {
if (PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(annotationMethod.getName())) {
@@ -32,6 +32,7 @@ import com.intellij.designer.designSurface.selection.NonResizeSelectionDecorator
import com.intellij.designer.designSurface.tools.ComponentCreationFactory;
import com.intellij.designer.designSurface.tools.ComponentPasteFactory;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.model.RadVisualComponent;
import com.intellij.designer.model.WrapInProvider;
import com.intellij.designer.palette.DefaultPaletteItem;
import com.intellij.designer.palette.PaletteGroup;
@@ -178,7 +179,7 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
createRenderer(parser.getLayoutXmlText(), new MyThrowable(), new ThrowableConsumer<RenderSession, Throwable>() {
@Override
public void consume(RenderSession session) throws Throwable {
RootView rootView = new RootView(session.getImage(), 30, 20);
RootView rootView = new RootView(30, 20, session.getImage());
try {
parser.updateRootComponent(session, rootView);
}
@@ -360,7 +361,7 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
private void removeNativeRoot() {
if (myRootComponent != null) {
Component component = ((RadViewComponent)myRootComponent).getNativeComponent();
Component component = ((RadVisualComponent)myRootComponent).getNativeComponent();
if (component != null) {
myLayeredPane.remove(component.getParent());
}
@@ -15,119 +15,38 @@
*/
package com.intellij.android.designer.designSurface;
import com.intellij.openapi.application.ModalityState;
import com.intellij.designer.designSurface.DesignerEditorPanel;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiTreeChangeAdapter;
import com.intellij.psi.PsiTreeChangeEvent;
import com.intellij.util.Alarm;
import com.intellij.util.containers.ComparatorUtil;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.annotations.NotNull;
/**
* @author Alexander Lobas
*/
public class ExternalPSIChangeListener extends PsiTreeChangeAdapter {
private final Alarm myAlarm = new Alarm();
private final AndroidDesignerEditorPanel myDesigner;
private final PsiFile myFile;
private final int myDelayMillis;
private final Runnable myRunnable;
private volatile boolean myRunState;
private volatile boolean myInitialize;
private String myContent;
public class ExternalPSIChangeListener extends com.intellij.designer.designSurface.ExternalPSIChangeListener {
private VirtualFile[] myResourceDepends;
private boolean myUpdateRenderer;
public ExternalPSIChangeListener(AndroidDesignerEditorPanel designer, PsiFile file, int delayMillis, Runnable runnable) {
myDesigner = designer;
myFile = file;
myDelayMillis = delayMillis;
myRunnable = runnable;
myContent = myDesigner.getEditorText();
PsiManager.getInstance(myDesigner.getProject()).addPsiTreeChangeListener(this);
}
public void setInitialize() {
myInitialize = true;
}
public void start() {
if (!myRunState) {
myRunState = true;
}
}
public void dispose() {
PsiManager.getInstance(myDesigner.getProject()).removePsiTreeChangeListener(this);
stop();
}
public void stop() {
if (myRunState) {
myRunState = false;
clear();
}
}
public void activate() {
if (!myRunState) {
start();
if (!ComparatorUtil.equalsNullable(myContent, myDesigner.getEditorText()) || myDesigner.getRootComponent() == null) {
myUpdateRenderer = false;
addRequest();
}
myContent = null;
}
public ExternalPSIChangeListener(DesignerEditorPanel designer, PsiFile file, int delayMillis, Runnable runnable) {
super(designer, file, delayMillis, runnable);
}
@Override
public void deactivate() {
if (myRunState) {
stop();
myContent = myDesigner.getEditorText();
}
myUpdateRenderer = false;
super.deactivate();
if (!myDesigner.getProject().isDisposed()) {
myResourceDepends = AndroidFacet.getInstance(myDesigner.getModule()).getLocalResourceManager().getAllResourceDirs();
}
}
public void addRequest() {
addRequest(myRunnable);
}
@Override
protected void updatePsi(PsiTreeChangeEvent event) {
boolean runState = myRunState;
super.updatePsi(event);
public boolean isUpdateRenderer() {
return myUpdateRenderer;
}
public void addRequest(final Runnable runnable) {
clear();
myAlarm.addRequest(new Runnable() {
@Override
public void run() {
if (myRunState && myInitialize && !myDesigner.getProject().isDisposed()) {
runnable.run();
}
}
}, myDelayMillis, ModalityState.stateForComponent(myDesigner));
}
public void clear() {
myAlarm.cancelAllRequests();
}
private void updatePsi(PsiTreeChangeEvent event) {
if (myRunState) {
if (myFile == event.getFile()) {
addRequest();
}
}
else if (myResourceDepends != null && !myUpdateRenderer) {
if (!runState && myResourceDepends != null && !myUpdateRenderer) {
PsiFile psiFile = event.getFile();
if (psiFile == null) {
return;
@@ -144,40 +63,4 @@ public class ExternalPSIChangeListener extends PsiTreeChangeAdapter {
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// PSI
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void childAdded(@NotNull PsiTreeChangeEvent event) {
updatePsi(event);
}
@Override
public void childRemoved(@NotNull PsiTreeChangeEvent event) {
updatePsi(event);
}
@Override
public void childReplaced(@NotNull PsiTreeChangeEvent event) {
updatePsi(event);
}
@Override
public void childMoved(@NotNull PsiTreeChangeEvent event) {
updatePsi(event);
}
@Override
public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
updatePsi(event);
}
@Override
public void propertyChanged(@NotNull PsiTreeChangeEvent event) {
updatePsi(event);
}
}
@@ -24,26 +24,16 @@ import java.util.List;
/**
* @author Alexander Lobas
*/
public class RootView extends JComponent {
private int myX;
private int myY;
private BufferedImage myImage;
public class RootView extends com.intellij.designer.designSurface.RootView {
private List<EmptyRegion> myEmptyRegions;
public RootView(BufferedImage image, int x, int y) {
myX = x;
myY = y;
setImage(image);
}
public BufferedImage getImage() {
return myImage;
public RootView(int x, int y, BufferedImage image) {
super(x, y, image);
}
public void setImage(BufferedImage image) {
myImage = image;
super.setImage(image);
myEmptyRegions = new ArrayList<EmptyRegion>();
setBounds(myX, myY, image.getWidth(), image.getHeight());
}
public void addEmptyRegion(int x, int y, int width, int height) {
@@ -61,7 +51,6 @@ public class RootView extends JComponent {
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(myImage, 0, 0, null);
for (EmptyRegion r : myEmptyRegions) {
g.setColor(r.myColor);
@@ -51,8 +51,8 @@ import org.jetbrains.android.actions.CreateXmlResourceDialog;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.resourceManagers.FileResourceProcessor;
import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.Nls;
@@ -603,25 +603,23 @@ public class ResourceDialog extends DialogWrapper implements TreeSelectionListen
public ResourceGroup(ResourceType type, ResourceManager manager) {
myType = type;
String resourceType = type.getName();
final String resourceType = type.getName();
Collection<String> resourceNames = manager.getValueResourceNames(resourceType);
for (String resourceName : resourceNames) {
myItems.add(new ResourceItem(this, resourceName, null, RESOURCE_ITEM_ICON));
}
final Set<String> fileNames = new HashSet<String>();
Set<String> fileNames = new HashSet<String>();
List<VirtualFile> dirs = manager.getResourceSubdirs(resourceType);
for (VirtualFile dir : dirs) {
for (VirtualFile resourceFile : dir.getChildren()) {
if (!resourceFile.isDirectory()) {
String fileName = AndroidCommonUtils.getResourceName(resourceType, resourceFile.getName());
if (fileNames.add(fileName)) {
myItems.add(new ResourceItem(this, fileName, resourceFile, resourceFile.getFileType().getIcon()));
}
manager.processFileResources(resourceType, new FileResourceProcessor() {
@Override
public boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) {
if (fileNames.add(resName)) {
myItems.add(new ResourceItem(ResourceGroup.this, resName, resFile, resFile.getFileType().getIcon()));
}
return true;
}
}
});
if (type == ResourceType.ID) {
for (String id : manager.getIds()) {
@@ -61,7 +61,7 @@ public class AndroidCommonUtils {
private static final Pattern ERROR_PATTERN = Pattern.compile(".*error.*");
private static final Pattern EXCEPTION_PATTERN = Pattern.compile(".*exception.*");
private static final Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class");
public static final Pattern R_PATTERN = Pattern.compile("R(\\$.*)?\\.class");
private static final Pattern MANIFEST_PATTERN = Pattern.compile("Manifest(\\$.*)?\\.class");
private static final String BUILD_CONFIG_CLASS_NAME = "BuildConfig.class";
+1
View File
@@ -143,6 +143,7 @@
<compiler implementation="org.jetbrains.android.compiler.AndroidPngFilesCachingCompiler"/>
<compiler implementation="org.jetbrains.android.compiler.AndroidResourcesPackagingCompiler"/>
<compiler implementation="org.jetbrains.android.compiler.AndroidPackagingCompiler"/>
<compiler.makeDependencyProcessor implementation="org.jetbrains.android.compiler.AndroidDependencyProcessor"/>
<completion.contributor language="XML" implementationClass="org.jetbrains.android.AndroidCompletionContributor"
id="compositieAttributeValues" order="first"/>
@@ -0,0 +1,88 @@
package org.jetbrains.android.compiler;
import com.intellij.compiler.DependencyProcessor;
import com.intellij.compiler.make.*;
import com.intellij.facet.ProjectFacetManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.ex.CompileContextEx;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.HashSet;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Collection;
import java.util.Set;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidDependencyProcessor implements DependencyProcessor {
@Override
public void processDependencies(final CompileContext context, int classQualifiedName, final CachingSearcher searcher)
throws CacheCorruptedException {
if (!(context instanceof CompileContextEx)) {
return;
}
final Project project = context.getProject();
if (!ProjectFacetManager.getInstance(project).hasFacets(AndroidFacet.ID)) {
return;
}
final DependencyCache depCache = ((CompileContextEx)context).getDependencyCache();
final Cache cache = depCache.getCache();
final String path = cache.getPath(classQualifiedName);
final String classFileName = new File(path).getName();
if (!AndroidCommonUtils.R_PATTERN.matcher(classFileName).matches()) {
return;
}
final String qName = depCache.resolve(classQualifiedName);
final int idx = qName.indexOf('$');
final String topLevelClassName = idx < 0 ? qName : qName.substring(0, idx);
final Set<String> qNamesToMark = new HashSet<String>();
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
final PsiClass[] classes =
JavaPsiFacade.getInstance(project).findClasses(topLevelClassName, GlobalSearchScope.allScope(project));
for (PsiClass aClass : classes) {
final Collection<PsiReference> references = searcher.findReferences(aClass, true);
for (PsiReference reference : references) {
final PsiClass ownerClass = getOwnerClass(reference.getElement());
if (ownerClass != null && !ownerClass.equals(aClass)) {
final String ownerClassQName = ownerClass.getQualifiedName();
if (ownerClassQName != null) {
qNamesToMark.add(ownerClassQName);
}
}
}
}
}
});
for (String toMark : qNamesToMark) {
final int qualifiedName = depCache.getSymbolTable().getId(toMark);
depCache.markClass(qualifiedName);
}
}
@Nullable
private static PsiClass getOwnerClass(PsiElement element) {
while (!(element instanceof PsiFile)) {
if (element instanceof PsiClass && element.getParent() instanceof PsiJavaFile) {
return (PsiClass)element;
}
element = element.getParent();
}
return null;
}
}
@@ -4,11 +4,8 @@ import com.android.resources.ResourceType;
import com.android.sdklib.IAndroidTarget;
import com.intellij.openapi.compiler.ValidityState;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.roots.impl.FileIndexImplUtil;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileFilter;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.android.dom.resources.DeclareStyleable;
@@ -16,6 +13,7 @@ import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.resources.Resources;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.resourceManagers.FileResourceProcessor;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.util.AndroidCommonUtils;
@@ -74,31 +72,24 @@ public class ResourceNamesValidityState implements ValidityState {
}
}
for (final VirtualFile subdir : manager.getResourceSubdirs(null)) {
final String subdirName = subdir.getName();
final int index = subdirName.indexOf('-');
final String typeName = index >= 0 ? subdirName.substring(0, index) : subdirName;
final ResourceType type = ResourceType.getEnum(typeName);
final boolean idProvidingResource = type != null && ArrayUtil.find(AndroidCommonUtils.ID_PROVIDING_RESOURCE_TYPES, type) >= 0;
manager.processFileResources(null, new FileResourceProcessor() {
@Override
public boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) {
final ResourceType type = ResourceType.getEnum(resFolderType);
final boolean idProvidingResource = type != null && ArrayUtil.find(AndroidCommonUtils.ID_PROVIDING_RESOURCE_TYPES, type) >= 0;
FileIndexImplUtil.iterateRecursively(subdir, VirtualFileFilter.ALL, new ContentIterator() {
@Override
public boolean processFile(VirtualFile fileOrDir) {
if (!fileOrDir.isDirectory()) {
ResourceFileData data = myResources.get(fileOrDir.getPath());
if (data == null) {
data = new ResourceFileData();
myResources.put(fileOrDir.getPath(), data);
}
if (idProvidingResource) {
data.setTimestamp(fileOrDir.getTimeStamp());
}
}
return true;
ResourceFileData data = myResources.get(resFile.getPath());
if (data == null) {
data = new ResourceFileData();
myResources.put(resFile.getPath(), data);
}
});
}
if (idProvidingResource) {
data.setTimestamp(resFile.getTimeStamp());
}
return true;
}
});
}
private static void addValueResources(VirtualFile file,
@@ -33,9 +33,9 @@ import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.inspections.CreateFileResourceQuickFix;
import org.jetbrains.android.inspections.CreateValueResourceQuickFix;
import org.jetbrains.android.resourceManagers.FileResourceProcessor;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -164,13 +164,15 @@ public class ResourceReferenceConverter extends ResolvingConverter<ResourceValue
final Set<String> result = new HashSet<String>();
final LocalResourceManager manager = facet.getLocalResourceManager();
for (VirtualFile resSubdir : manager.getResourceSubdirs(null)) {
final String resType = AndroidCommonUtils.getResourceTypeByDirName(resSubdir.getName());
if (resType != null && ResourceType.getEnum(resType) != null) {
result.add(resType);
manager.processFileResources(null, new FileResourceProcessor() {
@Override
public boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) {
if (ResourceType.getEnum(resFolderType) != null) {
result.add(resFolderType);
}
return true;
}
}
});
result.addAll(manager.getValueResourceTypes());
if (manager.getIds().size() > 0) {
@@ -0,0 +1,11 @@
package org.jetbrains.android.resourceManagers;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene.Kudelevsky
*/
public interface FileResourceProcessor {
boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType);
}
@@ -75,6 +75,30 @@ public abstract class ResourceManager {
@Nullable
public abstract VirtualFile getResourceDir();
public boolean processFileResources(@Nullable String resourceType, @NotNull FileResourceProcessor processor) {
return processFileResources(resourceType, processor, true);
}
public boolean processFileResources(@Nullable String resourceType, @NotNull FileResourceProcessor processor, boolean publicOnly) {
for (VirtualFile resSubdir : getResourceSubdirs(resourceType)) {
final String resType = AndroidCommonUtils.getResourceTypeByDirName(resSubdir.getName());
if (resType != null) {
assert resourceType == null || resourceType.equals(resType);
for (VirtualFile resFile : resSubdir.getChildren()) {
final String resName = AndroidCommonUtils.getResourceName(resType, resFile.getName());
if (!resFile.isDirectory() && (!publicOnly || isResourcePublic(resType, resName))) {
if (!processor.process(resFile, resName, resType)) {
return false;
}
}
}
}
}
return true;
}
public boolean isResourceDir(@NotNull VirtualFile dir) {
return dir.equals(getResourceDir());
}
@@ -84,38 +108,44 @@ public abstract class ResourceManager {
return VirtualFile.EMPTY_ARRAY;
}
protected boolean isResourcePublic(@NotNull String type, @NotNull String name) {
return true;
}
@NotNull
public List<VirtualFile> getResourceSubdirs(@Nullable String resourceType) {
return AndroidResourceUtil.getResourceSubdirs(resourceType, getAllResourceDirs());
}
@NotNull
public List<PsiFile> findResourceFiles(@NotNull String resType,
@Nullable String resName,
boolean distinguishDelimetersInName,
@NotNull String... extensions) {
List<PsiFile> result = new ArrayList<PsiFile>();
Set<String> extensionSet = new HashSet<String>();
public List<PsiFile> findResourceFiles(@NotNull final String resType1,
@Nullable final String resName1,
final boolean distinguishDelimetersInName,
@NotNull final String... extensions) {
final List<PsiFile> result = new ArrayList<PsiFile>();
final Set<String> extensionSet = new HashSet<String>();
addAll(extensionSet, extensions);
for (VirtualFile dir : getResourceSubdirs(resType)) {
for (final VirtualFile resFile : dir.getChildren()) {
String extension = resFile.getExtension();
if (extensions.length == 0 || extensionSet.contains(extension)) {
String s = AndroidCommonUtils.getResourceName(resType, resFile.getName());
if (resName == null || AndroidUtils.equal(resName, s, distinguishDelimetersInName)) {
PsiFile file = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
@Nullable
public PsiFile compute() {
return PsiManager.getInstance(myModule.getProject()).findFile(resFile);
}
});
if (file != null) {
result.add(file);
processFileResources(resType1, new FileResourceProcessor() {
@Override
public boolean process(@NotNull final VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) {
final String extension = resFile.getExtension();
if ((extensions.length == 0 || extensionSet.contains(extension)) &&
(resName1 == null || AndroidUtils.equal(resName1, resName, distinguishDelimetersInName))) {
final PsiFile file = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
@Nullable
public PsiFile compute() {
return PsiManager.getInstance(myModule.getProject()).findFile(resFile);
}
});
if (file != null) {
result.add(file);
}
}
return true;
}
}
});
return result;
}
@@ -171,7 +201,14 @@ public abstract class ResourceManager {
if (!resources.isValid() || myModule.isDisposed() || myModule.getProject().isDisposed()) {
return;
}
result.addAll(AndroidResourceUtil.getValueResourcesFromElement(resourceType, resources));
final List<ResourceElement> valueResources = AndroidResourceUtil.getValueResourcesFromElement(resourceType, resources);
for (ResourceElement valueResource : valueResources) {
final String resName = valueResource.getName().getValue();
if (resName != null && isResourcePublic(resourceType, resName)) {
result.add(valueResource);
}
}
}
});
}
@@ -207,15 +244,16 @@ public abstract class ResourceManager {
}
@NotNull
public Set<String> getFileResourcesNames(@NotNull String resourceType) {
Set<String> result = new HashSet<String>();
List<VirtualFile> dirs = getResourceSubdirs(resourceType);
for (VirtualFile dir : dirs) {
for (VirtualFile resourceFile : dir.getChildren()) {
if (resourceFile.isDirectory()) continue;
result.add(AndroidCommonUtils.getResourceName(resourceType, resourceFile.getName()));
public Set<String> getFileResourcesNames(@NotNull final String resourceType) {
final Set<String> result = new HashSet<String>();
processFileResources(resourceType, new FileResourceProcessor() {
@Override
public boolean process(@NotNull VirtualFile resFile, @NotNull String resName, @NotNull String resFolderType) {
result.add(resName);
return true;
}
}
});
return result;
}
@@ -269,7 +307,9 @@ public abstract class ResourceManager {
if (entries != null) {
for (ResourceEntry entry : entries) {
result.add(entry);
if (isResourcePublic(entry.getType(), entry.getName())) {
result.add(entry);
}
}
}
}
@@ -293,6 +333,10 @@ public abstract class ResourceManager {
// searches only declarations such as "@+id/..."
@Nullable
public List<PsiElement> findIdDeclarations(@NotNull final String id) {
if (!isResourcePublic(ResourceType.ID.getName(), id)) {
return Collections.emptyList();
}
final List<PsiElement> declarations = new ArrayList<PsiElement>();
final Collection<VirtualFile> files =
FileBasedIndex.getInstance().getContainingFiles(AndroidIdIndex.INDEX_ID, id, GlobalSearchScope.allScope(myModule.getProject()));
@@ -357,9 +401,13 @@ public abstract class ResourceManager {
for (VirtualFile resSubdir : getResourceSubdirsToSearchIds()) {
for (VirtualFile resFile : resSubdir.getChildren()) {
final Set<String> ids = file2ids.get(resFile);
if (ids != null) {
result.addAll(ids);
for (String id : ids) {
if (isResourcePublic(ResourceType.ID.getName(), id)) {
result.add(id);
}
}
}
}
}
@@ -16,25 +16,84 @@
package org.jetbrains.android.resourceManagers;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import com.intellij.util.xml.ConvertContext;
import org.jetbrains.android.dom.attrs.AttributeDefinitions;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.sdk.AndroidTargetData;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
/**
* @author coyote
*/
public class SystemResourceManager extends ResourceManager {
private final AndroidPlatform myPlatform;
private final Map<String, Set<String>> myPublicResourcesCache = new HashMap<String, Set<String>>();
public SystemResourceManager(@NotNull AndroidFacet facet, @NotNull AndroidPlatform androidPlatform) {
super(facet);
myPlatform = androidPlatform;
final Module module = facet.getModule();
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
buildPublicResourceCache(module);
}
});
}
private void buildPublicResourceCache(@NotNull Module module) {
final PsiClass rClass = JavaPsiFacade.getInstance(module.getProject())
.findClass(SdkConstants.CLASS_R, module.getModuleWithDependenciesAndLibrariesScope(false));
if (rClass == null) {
return;
}
for (PsiClass resourceClass : rClass.getInnerClasses()) {
final String resType = resourceClass.getName();
if (resType != null) {
Set<String> resNameSet = myPublicResourcesCache.get(resType);
if (resNameSet == null) {
resNameSet = new HashSet<String>();
myPublicResourcesCache.put(resType, resNameSet);
}
for (PsiField resField : resourceClass.getFields()) {
final String resName = resField.getName();
if (resName != null) {
resNameSet.add(resName);
}
}
}
}
}
protected boolean isResourcePublic(@NotNull String type, @NotNull String name) {
final Set<String> fieldNames = myPublicResourcesCache.get(type);
if (fieldNames == null || fieldNames.isEmpty()) {
return false;
}
final String fieldName = AndroidResourceUtil.getFieldNameByResourceName(name);
return fieldNames.contains(fieldName);
}
@NotNull
+2 -2
View File
@@ -1,12 +1,12 @@
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="@android:interpolator/platformInterpolator"
android:interpolator="@android:interpolator/accelerate_cubic"
android:valueFrom="100dp" android:valueTo="0dp"
android:valueType="floatType"
android:propertyName="translationX"
android:duration="@android:integer/config_mediumAnimTime" />
<animator
android:interpolator="@android:interpolator/platformInterpolator"
android:interpolator="@android:interpolator/accelerate_cubic"
android:valueFrom="0.0" android:valueTo="1.0"
android:valueType="floatType"
android:propertyName="alpha"
@@ -0,0 +1,14 @@
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="@interpolator/myInterpolator"
android:valueFrom="100dp" android:valueTo="0dp"
android:valueType="floatType"
android:propertyName="translationX"
android:duration="@android:integer/config_mediumAnimTime" />
<animator
android:interpolator="@interpolator/myInterpolator"
android:valueFrom="0.0" android:valueTo="1.0"
android:valueType="floatType"
android:propertyName="alpha"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
@@ -1,3 +1,3 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:layout_alignBaseline="@android:id/no_permiss<caret>"/>
<TextView android:layout_alignBaseline="@android:id/closeBut<caret>"/>
</RelativeLayout>
@@ -1,3 +1,3 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:layout_alignBaseline="@android:id/no_permissions"/>
<TextView android:layout_alignBaseline="@android:id/closeButton"/>
</RelativeLayout>
@@ -1,3 +1,3 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@android:style/WindowTitleBackground">
style="@android:style/MediaButton">
</LinearLayout>
@@ -0,0 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="<error>@android:drawable/private</error>"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/menuitem_background"/>
</LinearLayout>
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="p1.p2">
<application android:icon="@drawable/picture1">
<activity android:name="MyActivity"/>
</application>
</manifest>

Some files were not shown because too many files have changed in this diff Show More