diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java index 004209e3930c..b5526f0efab4 100644 --- a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java +++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java @@ -857,12 +857,13 @@ public class AsmCodeGenerator { } pushBorderProperties(container, generator, borderTitle, componentLocal); - if (Boolean.valueOf(System.getProperty("idea.is.internal")).booleanValue()) { - container.getDelegeeClientProperties().put(ourBorderFactoryClientProperty, StringDescriptor.create("com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent")); - } Type borderFactoryType = ourBorderFactoryType; StringDescriptor borderFactoryValue = (StringDescriptor)container.getDelegeeClientProperties().get(ourBorderFactoryClientProperty); + if (borderFactoryValue == null && Boolean.valueOf(System.getProperty("idea.is.internal")).booleanValue()) { + borderFactoryValue = StringDescriptor.create("com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"); + container.getDelegeeClientProperties().put(ourBorderFactoryClientProperty, borderFactoryValue); + } if (borderFactoryValue != null && borderFactoryValue.getValue().length() != 0) { borderFactoryType = typeFromClassName(borderFactoryValue.getValue()); } diff --git a/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java new file mode 100644 index 000000000000..6d62805915b8 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi; + +import org.jetbrains.annotations.Nullable; + +/** + * Represents a method or constructor reference. + */ +public interface PsiMethodReferenceExpression extends PsiReferenceExpression { + /** + * Returns the type element used as the qualifier of the reference. + * + * @return the qualifier, or null if the reference is qualified by expression. + */ + @Nullable + PsiTypeElement getQualifierType(); +} diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index b65578b6df00..7e522b6a46b4 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -321,6 +321,7 @@ public class ExpressionParser { private enum BreakPoint {P1, P2, P3, P4} + // todo[r.sh] make 'this', 'super' and 'class' reference expressions @Nullable private PsiBuilder.Marker parsePrimary(final PsiBuilder builder, @Nullable final BreakPoint breakPoint, final int breakOffset) { PsiBuilder.Marker startMarker = builder.mark(); @@ -350,7 +351,7 @@ public class ExpressionParser { final int offset = builder.getCurrentOffset(); startMarker.rollbackTo(); - final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); + final PsiBuilder.Marker classObjAccess = parseClassAccessOrMethodReference(builder); if (classObjAccess == null || builder.getCurrentOffset() < offset) { copy.rollbackTo(); return parsePrimary(builder, BreakPoint.P1, offset); @@ -466,7 +467,7 @@ public class ExpressionParser { final PsiBuilder.Marker copy = startMarker.precede(); startMarker.rollbackTo(); - final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); + final PsiBuilder.Marker classObjAccess = parseClassAccessOrMethodReference(builder); if (classObjAccess == null || builder.getCurrentOffset() <= pos) { copy.rollbackTo(); return parsePrimary(builder, BreakPoint.P4, -1); @@ -498,6 +499,9 @@ public class ExpressionParser { expr = arrayAccess; } } + else if (tokenType == JavaTokenType.DOUBLE_COLON) { + return parseMethodReference(builder, startMarker); + } else { startMarker.drop(); return expr; @@ -515,6 +519,7 @@ public class ExpressionParser { literal.done(JavaElementType.LITERAL_EXPRESSION); return literal; } + if (tokenType == JavaTokenType.LPARENTH) { final PsiBuilder.Marker parenth = builder.mark(); builder.advanceLexer(); @@ -533,12 +538,25 @@ public class ExpressionParser { parenth.done(JavaElementType.PARENTH_EXPRESSION); return parenth; } + if (tokenType == JavaTokenType.LBRACE) { return parseArrayInitializer(builder); } + if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType) || tokenType == JavaTokenType.IDENTIFIER) { + final boolean primitive = tokenType != JavaTokenType.IDENTIFIER; + final PsiBuilder.Marker mark = builder.mark(); + + final ReferenceParser.TypeInfo typeInfo = myReferenceParser.parseTypeInfo(builder, 0); + if (typeInfo != null && (primitive || !typeInfo.hasErrors && typeInfo.isParameterized)) { + final PsiBuilder.Marker result = continueClassAccessOrMethodReference(builder, mark, primitive); + if (result != null) return result; + } + + mark.rollbackTo(); + } + PsiBuilder.Marker annotation = null; - final PsiBuilder.Marker beforeAnnotation = builder.mark(); if (tokenType == JavaTokenType.AT) { annotation = myDeclarationParser.parseAnnotations(builder); tokenType = builder.getTokenType(); @@ -558,17 +576,13 @@ public class ExpressionParser { builder.advanceLexer(); refExpr.done(JavaElementType.REFERENCE_EXPRESSION); - beforeAnnotation.drop(); return refExpr; } if (annotation != null) { - beforeAnnotation.rollbackTo(); + annotation.rollbackTo(); tokenType = builder.getTokenType(); } - else { - beforeAnnotation.drop(); - } PsiBuilder.Marker expr = null; if (tokenType == JavaTokenType.LT) { @@ -599,12 +613,10 @@ public class ExpressionParser { : JavaElementType.SUPER_EXPRESSION); return expr; } + if (tokenType == JavaTokenType.NEW_KEYWORD) { return parseNew(builder, null); } - if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { - return parseClassObjectAccess(builder); - } return null; } @@ -745,27 +757,47 @@ public class ExpressionParser { } @Nullable - private PsiBuilder.Marker parseClassObjectAccess(final PsiBuilder builder) { + private PsiBuilder.Marker parseClassAccessOrMethodReference(final PsiBuilder builder) { final PsiBuilder.Marker expr = builder.mark(); + final boolean primitive = ElementType.PRIMITIVE_TYPE_BIT_SET.contains(builder.getTokenType()); if (myReferenceParser.parseType(builder, 0) == null) { expr.drop(); return null; } - if (builder.getTokenType() != JavaTokenType.DOT) { - expr.rollbackTo(); - return null; + final PsiBuilder.Marker result = continueClassAccessOrMethodReference(builder, expr, primitive); + if (result == null) expr.rollbackTo(); + return result; + } + + @Nullable + private PsiBuilder.Marker continueClassAccessOrMethodReference(final PsiBuilder builder, + final PsiBuilder.Marker expr, + final boolean primitive) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == JavaTokenType.DOT) { + return parseClassObjectAccess(builder, expr, primitive); } - PsiBuilder.Marker afterType = builder.mark(); + else if (tokenType == JavaTokenType.DOUBLE_COLON) { + return parseMethodReference(builder, expr); + } + + return null; + } + + @Nullable + private static PsiBuilder.Marker parseClassObjectAccess(PsiBuilder builder, PsiBuilder.Marker expr, boolean primitive) { + final PsiBuilder.Marker mark = builder.mark(); builder.advanceLexer(); if (builder.getTokenType() == JavaTokenType.CLASS_KEYWORD) { - afterType.drop(); + mark.drop(); builder.advanceLexer(); } else { - afterType.rollbackTo(); + if (!primitive) return null; + mark.rollbackTo(); builder.error(".class expected"); } @@ -773,6 +805,20 @@ public class ExpressionParser { return expr; } + @NotNull + private PsiBuilder.Marker parseMethodReference(final PsiBuilder builder, final PsiBuilder.Marker start) { + builder.advanceLexer(); + + myReferenceParser.parseReferenceParameterList(builder, false, false); + + if (!expect(builder, JavaTokenType.IDENTIFIER) && !expect(builder, JavaTokenType.NEW_KEYWORD)) { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + + start.done(JavaElementType.METHOD_REF_EXPRESSION); + return start; + } + @NotNull public PsiBuilder.Marker parseArgumentList(final PsiBuilder builder) { final PsiBuilder.Marker list = builder.mark(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java index 48b07d17942c..a734707a2da1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/ElementType.java @@ -29,13 +29,12 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, JavaElemen TokenSet KEYWORD_BIT_SET = TokenSet.create( ABSTRACT_KEYWORD, ASSERT_KEYWORD, BOOLEAN_KEYWORD, BREAK_KEYWORD, BYTE_KEYWORD, CASE_KEYWORD, CATCH_KEYWORD, CHAR_KEYWORD, - CLASS_KEYWORD, - CONST_KEYWORD, CONTINUE_KEYWORD, DEFAULT_KEYWORD, DO_KEYWORD, DOUBLE_KEYWORD, ELSE_KEYWORD, ENUM_KEYWORD, EXTENDS_KEYWORD, - FINAL_KEYWORD, - FINALLY_KEYWORD, FLOAT_KEYWORD, FOR_KEYWORD, GOTO_KEYWORD, IF_KEYWORD, IMPLEMENTS_KEYWORD, IMPORT_KEYWORD, INSTANCEOF_KEYWORD, - INT_KEYWORD, INTERFACE_KEYWORD, LONG_KEYWORD, NATIVE_KEYWORD, NEW_KEYWORD, PACKAGE_KEYWORD, PRIVATE_KEYWORD, PROTECTED_KEYWORD, - PUBLIC_KEYWORD, RETURN_KEYWORD, SHORT_KEYWORD, SUPER_KEYWORD, STATIC_KEYWORD, STRICTFP_KEYWORD, SWITCH_KEYWORD, SYNCHRONIZED_KEYWORD, - THIS_KEYWORD, THROW_KEYWORD, THROWS_KEYWORD, TRANSIENT_KEYWORD, TRY_KEYWORD, VOID_KEYWORD, VOLATILE_KEYWORD, WHILE_KEYWORD); + CLASS_KEYWORD, CONST_KEYWORD, CONTINUE_KEYWORD, DEFAULT_KEYWORD, DO_KEYWORD, DOUBLE_KEYWORD, ELSE_KEYWORD, ENUM_KEYWORD, + EXTENDS_KEYWORD, FINAL_KEYWORD, FINALLY_KEYWORD, FLOAT_KEYWORD, FOR_KEYWORD, GOTO_KEYWORD, IF_KEYWORD, IMPLEMENTS_KEYWORD, + IMPORT_KEYWORD, INSTANCEOF_KEYWORD, INT_KEYWORD, INTERFACE_KEYWORD, LONG_KEYWORD, NATIVE_KEYWORD, NEW_KEYWORD, PACKAGE_KEYWORD, + PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, RETURN_KEYWORD, SHORT_KEYWORD, SUPER_KEYWORD, STATIC_KEYWORD, STRICTFP_KEYWORD, + SWITCH_KEYWORD, SYNCHRONIZED_KEYWORD, THIS_KEYWORD, THROW_KEYWORD, THROWS_KEYWORD, TRANSIENT_KEYWORD, TRY_KEYWORD, VOID_KEYWORD, + VOLATILE_KEYWORD, WHILE_KEYWORD); TokenSet LITERAL_BIT_SET = TokenSet.create(TRUE_KEYWORD, FALSE_KEYWORD, NULL_KEYWORD); @@ -45,33 +44,31 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, JavaElemen PLUSEQ, MINUSEQ, ASTERISKEQ, DIVEQ, ANDEQ, OREQ, XOREQ, PERCEQ, LTLTEQ, GTGTEQ, GTGTGTEQ); TokenSet MODIFIER_BIT_SET = TokenSet.create( - PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, STATIC_KEYWORD, ABSTRACT_KEYWORD, FINAL_KEYWORD, NATIVE_KEYWORD, SYNCHRONIZED_KEYWORD, - STRICTFP_KEYWORD, TRANSIENT_KEYWORD, VOLATILE_KEYWORD); + PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, STATIC_KEYWORD, ABSTRACT_KEYWORD, FINAL_KEYWORD, NATIVE_KEYWORD, + SYNCHRONIZED_KEYWORD, STRICTFP_KEYWORD, TRANSIENT_KEYWORD, VOLATILE_KEYWORD); TokenSet PRIMITIVE_TYPE_BIT_SET = TokenSet.create( BOOLEAN_KEYWORD, BYTE_KEYWORD, SHORT_KEYWORD, INT_KEYWORD, LONG_KEYWORD, CHAR_KEYWORD, FLOAT_KEYWORD, DOUBLE_KEYWORD, VOID_KEYWORD); TokenSet EXPRESSION_BIT_SET = TokenSet.create( REFERENCE_EXPRESSION, LITERAL_EXPRESSION, THIS_EXPRESSION, SUPER_EXPRESSION, PARENTH_EXPRESSION, METHOD_CALL_EXPRESSION, - TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, ASSIGNMENT_EXPRESSION, - NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, ARRAY_INITIALIZER_EXPRESSION, INSTANCE_OF_EXPRESSION, CLASS_OBJECT_ACCESS_EXPRESSION, - EMPTY_EXPRESSION); + TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, + ASSIGNMENT_EXPRESSION, NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, ARRAY_INITIALIZER_EXPRESSION, INSTANCE_OF_EXPRESSION, + CLASS_OBJECT_ACCESS_EXPRESSION, METHOD_REF_EXPRESSION, EMPTY_EXPRESSION); TokenSet ANNOTATION_MEMBER_VALUE_BIT_SET = TokenSet.orSet(EXPRESSION_BIT_SET, TokenSet.create(ANNOTATION, ANNOTATION_ARRAY_INITIALIZER)); TokenSet ARRAY_DIMENSION_BIT_SET = TokenSet.create( REFERENCE_EXPRESSION, LITERAL_EXPRESSION, THIS_EXPRESSION, SUPER_EXPRESSION, PARENTH_EXPRESSION, METHOD_CALL_EXPRESSION, - TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, ASSIGNMENT_EXPRESSION, - NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, INSTANCE_OF_EXPRESSION, CLASS_OBJECT_ACCESS_EXPRESSION, EMPTY_EXPRESSION); + TYPE_CAST_EXPRESSION, PREFIX_EXPRESSION, POSTFIX_EXPRESSION, BINARY_EXPRESSION, POLYADIC_EXPRESSION, CONDITIONAL_EXPRESSION, + ASSIGNMENT_EXPRESSION, NEW_EXPRESSION, ARRAY_ACCESS_EXPRESSION, INSTANCE_OF_EXPRESSION, CLASS_OBJECT_ACCESS_EXPRESSION, + EMPTY_EXPRESSION); TokenSet JAVA_STATEMENT_BIT_SET = TokenSet.create( - EMPTY_STATEMENT, BLOCK_STATEMENT, EXPRESSION_STATEMENT, EXPRESSION_LIST_STATEMENT, - DECLARATION_STATEMENT, IF_STATEMENT, WHILE_STATEMENT, FOR_STATEMENT, FOREACH_STATEMENT, - DO_WHILE_STATEMENT, SWITCH_STATEMENT, SWITCH_LABEL_STATEMENT, BREAK_STATEMENT, - CONTINUE_STATEMENT, RETURN_STATEMENT, THROW_STATEMENT, SYNCHRONIZED_STATEMENT, - TRY_STATEMENT, LABELED_STATEMENT, ASSERT_STATEMENT - ); + EMPTY_STATEMENT, BLOCK_STATEMENT, EXPRESSION_STATEMENT, EXPRESSION_LIST_STATEMENT, DECLARATION_STATEMENT, IF_STATEMENT, + WHILE_STATEMENT, FOR_STATEMENT, FOREACH_STATEMENT, DO_WHILE_STATEMENT, SWITCH_STATEMENT, SWITCH_LABEL_STATEMENT, BREAK_STATEMENT, + CONTINUE_STATEMENT, RETURN_STATEMENT, THROW_STATEMENT, SYNCHRONIZED_STATEMENT, TRY_STATEMENT, LABELED_STATEMENT, ASSERT_STATEMENT); TokenSet TYPES_BIT_SET = TokenSet.create(TYPE); TokenSet IMPORT_STATEMENT_BIT_SET = TokenSet.create(IMPORT_STATEMENT); @@ -79,7 +76,6 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType, JavaElemen TokenSet IMPORT_STATEMENT_BASE_BIT_SET = TokenSet.create(IMPORT_STATEMENT, IMPORT_STATIC_STATEMENT); TokenSet CLASS_KEYWORD_BIT_SET = TokenSet.create(CLASS_KEYWORD, INTERFACE_KEYWORD, ENUM_KEYWORD); TokenSet MEMBER_BIT_SET = TokenSet.create(CLASS, FIELD, ENUM_CONSTANT, METHOD, ANNOTATION_METHOD); - TokenSet FULL_MEMBER_BIT_SET = TokenSet.orSet(MEMBER_BIT_SET, - TokenSet.create(CLASS_INITIALIZER)); + TokenSet FULL_MEMBER_BIT_SET = TokenSet.orSet(MEMBER_BIT_SET, TokenSet.create(CLASS_INITIALIZER)); TokenSet ANNOTATIONS = TokenSet.create(ANNOTATION); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 69476e9ef24d..7f96a2d003dd 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -104,6 +104,7 @@ public interface JavaElementType { IElementType INSTANCE_OF_EXPRESSION = new JavaCompositeElementType("INSTANCE_OF_EXPRESSION", PsiInstanceOfExpressionImpl.class); IElementType CLASS_OBJECT_ACCESS_EXPRESSION = new JavaCompositeElementType("CLASS_OBJECT_ACCESS_EXPRESSION", PsiClassObjectAccessExpressionImpl.class); IElementType EMPTY_EXPRESSION = new JavaCompositeElementType("EMPTY_EXPRESSION", PsiEmptyExpressionImpl.class, true); + IElementType METHOD_REF_EXPRESSION = new JavaCompositeElementType("METHOD_REF_EXPRESSION", PsiMethodReferenceExpressionImpl.class); IElementType EXPRESSION_LIST = new JavaCompositeElementType("EXPRESSION_LIST", PsiExpressionListImpl.class, true); IElementType EMPTY_STATEMENT = new JavaCompositeElementType("EMPTY_STATEMENT", PsiEmptyStatementImpl.class); IElementType BLOCK_STATEMENT = new JavaCompositeElementType("BLOCK_STATEMENT", PsiBlockStatementImpl.class); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java new file mode 100644 index 000000000000..ad21de8a54f2 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.tree.java; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.NotNull; + +public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiMethodReferenceExpression { + private static Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl"); + + public PsiMethodReferenceExpressionImpl() { + super(JavaElementType.METHOD_REF_EXPRESSION); + } + + @Override + public PsiTypeElement getQualifierType() { + final PsiElement qualifier = getQualifier(); + return qualifier instanceof PsiTypeElement ? (PsiTypeElement)qualifier : null; + } + + @Override + public PsiExpression getQualifierExpression() { + final PsiElement qualifier = getQualifier(); + return qualifier instanceof PsiExpression ? (PsiExpression)qualifier : null; + } + + @Override + public PsiType getType() { + // todo[r.sh]: implement + return null; + } + + @Override + public PsiElement getReferenceNameElement() { + final PsiElement element = getLastChild(); + return element instanceof PsiIdentifier || PsiUtil.isJavaToken(element, JavaTokenType.NEW_KEYWORD) ? element : null; + } + + @Override + public void processVariants(final PsiScopeProcessor processor) { + // todo[r.sh]: implement + } + + @NotNull + @Override + public JavaResolveResult[] multiResolve(final boolean incompleteCode) { + // todo[r.sh]: implement + return JavaResolveResult.EMPTY_ARRAY; + } + + @Override + public PsiElement getQualifier() { + final PsiElement element = getFirstChild(); + return element instanceof PsiExpression || element instanceof PsiTypeElement ? element : null; + } + + @Override + public TextRange getRangeInElement() { + final PsiElement element = getReferenceNameElement(); + if (element != null) return new TextRange(element.getStartOffsetInParent(), element.getTextLength()); + final PsiElement colons = findPsiChildByType(JavaTokenType.DOUBLE_COLON); + if (colons != null) return new TextRange(colons.getStartOffsetInParent(), colons.getTextLength()); + LOG.error(getText()); + return null; + } + + @NotNull + @Override + public String getCanonicalText() { + return getText(); + } + + @Override + public boolean isReferenceTo(final PsiElement element) { + if (!(element instanceof PsiMethod)) return false; + final PsiMethod method = (PsiMethod)element; + + final PsiElement nameElement = getReferenceNameElement(); + if (nameElement instanceof PsiIdentifier) { + if (!nameElement.getText().equals(method.getName())) return false; + } + else if (PsiUtil.isJavaToken(nameElement, JavaTokenType.NEW_KEYWORD)) { + if (!method.isConstructor()) return false; + } + + return element.getManager().areElementsEquivalent(element, resolve()); + } + + @Override + public String toString() { + return "PsiMethodReferenceExpression:" + getText(); + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionBase.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionBase.java new file mode 100644 index 000000000000..df67a536d105 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionBase.java @@ -0,0 +1,117 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.source.tree.java; + +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class PsiReferenceExpressionBase extends ExpressionPsiElement implements PsiReferenceExpression { + public PsiReferenceExpressionBase(@NotNull final IElementType type) { + super(type); + } + + @Override + public PsiElement bindToElementViaStaticImport(@NotNull final PsiClass qualifierClass) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + @Override + public void setQualifierExpression(@Nullable PsiExpression newQualifier) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + @Override + public PsiElement getElement() { + return this; + } + + @Override + public PsiElement resolve() { + return advancedResolve(false).getElement(); + } + + @Override + public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + @Override + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + @Override + public boolean isReferenceTo(final PsiElement element) { + return element.getManager().areElementsEquivalent(element, resolve()); + } + + @NotNull + @Override + public Object[] getVariants() { + // this reference's variants are rather obtained with processVariants() + return ArrayUtil.EMPTY_OBJECT_ARRAY; + } + + @Override + public boolean isSoft() { + return false; + } + + @Override + public PsiReference getReference() { + return this; + } + + @NotNull + @Override + public JavaResolveResult advancedResolve(boolean incompleteCode) { + final JavaResolveResult[] results = multiResolve(incompleteCode); + return results.length == 1 ? results[0] : JavaResolveResult.EMPTY; + } + + @Override + public String getReferenceName() { + final PsiElement element = getReferenceNameElement(); + return element != null ? element.getText() : null; + } + + @Override + public PsiReferenceParameterList getParameterList() { + return PsiTreeUtil.getChildOfType(this, PsiReferenceParameterList.class); + } + + @NotNull + @Override + public PsiType[] getTypeParameters() { + final PsiReferenceParameterList parameterList = getParameterList(); + return parameterList != null ? parameterList.getTypeArguments() : PsiType.EMPTY_ARRAY; + } + + @Override + public boolean isQualified() { + return getQualifier() != null; + } + + @Override + public String getQualifiedName() { + return getCanonicalText(); + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java index 52472d402077..9974e27bfe30 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,7 +60,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements PsiReferenceExpression, SourceJavaCodeReference { +public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiReferenceExpression, SourceJavaCodeReference { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl"); private volatile String myCachedQName = null; @@ -108,7 +108,8 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements } else { PsiManagerEx manager = getManager(); - PsiReferenceExpression classRef = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createReferenceExpression(qualifierClass); + PsiReferenceExpression classRef = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createReferenceExpression( + qualifierClass); final CharTable treeCharTab = SharedImplUtil.findCharTableByTree(this); LeafElement dot = Factory.createSingleLeafElement(JavaTokenType.DOT, ".", 0, 1, treeCharTab, manager); addInternal(dot, dot, SourceTreeToPsiMap.psiElementToTree(getParameterList()), Boolean.TRUE); @@ -122,7 +123,8 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements final String qualifiedName = qualifierClass.getQualifiedName(); final List refs = getImportsFromClass(importList, qualifiedName); if (refs.size() < JavaCodeStyleSettingsFacade.getInstance(qualifierClass.getProject()).getNamesCountToUseImportOnDemand()) { - importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, staticName)); + importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, + staticName)); } else { for (PsiJavaCodeReferenceElement ref : refs) { final PsiImportStaticStatement importStatement = PsiTreeUtil.getParentOfType(ref, PsiImportStaticStatement.class); @@ -130,7 +132,8 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements importStatement.delete(); } } - importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, "*")); + importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, + "*")); } } @@ -174,16 +177,6 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements return getQualifierExpression(); } - @Override - public PsiReference getReference() { - return this; - } - - @Override - public PsiElement resolve() { - return advancedResolve(false).getElement(); - } - @Override public void clearCaches() { myCachedQName = null; @@ -316,18 +309,6 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements return getCachedTextSkipWhiteSpaceAndComments(); } - @Override - public String getQualifiedName() { - return getCanonicalText(); - } - - @Override - public String getReferenceName() { - PsiElement element = getReferenceNameElement(); - if (element == null) return null; - return element.getText(); - } - private final Function ourTypeEvaluator = new TypeEvaluator(); private static class TypeEvaluator implements NullableFunction { @@ -409,20 +390,7 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements // optimization: methodCallExpression should resolve to a method if (parentIsMethodCall != resolvingToMethod) return false; - return element.getManager().areElementsEquivalent(element, resolve()); - } - - - @Override - @NotNull - public Object[] getVariants() { - //this reference's variants are rather obtained with processVariants() - return ArrayUtil.EMPTY_OBJECT_ARRAY; - } - - @Override - public boolean isSoft() { - return false; + return super.isReferenceTo(element); } @Override @@ -435,7 +403,7 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements filter.addFilter(new AndFilter(ElementClassFilter.METHOD, new NotFilter(new ConstructorFilter()))); filter.addFilter(ElementClassFilter.VARIABLE); - FilterScopeProcessor proc = new FilterScopeProcessor(filter, processor) { + FilterScopeProcessor filterProcessor = new FilterScopeProcessor(filter, processor) { private final Set myVarNames = new THashSet(); @Override @@ -454,7 +422,7 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements } }; - PsiScopesUtil.resolveAndWalk(proc, this, null, true); + PsiScopesUtil.resolveAndWalk(filterProcessor, this, null, true); } private static boolean seemsScrambled(PsiClass element) { @@ -469,24 +437,11 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements Character.isLowerCase(qualifiedName.charAt(0)); } - @Override - @NotNull - public JavaResolveResult advancedResolve(boolean incompleteCode) { - final JavaResolveResult[] results = multiResolve(incompleteCode); - if (results.length == 1) return results[0]; - return JavaResolveResult.EMPTY; - } - @Override public PsiElement getReferenceNameElement() { return findChildByRoleAsPsiElement(ChildRole.REFERENCE_NAME); } - @Override - public PsiReferenceParameterList getParameterList() { - return (PsiReferenceParameterList)findChildByRoleAsPsiElement(ChildRole.REFERENCE_PARAMETER_LIST); - } - @Override public int getTextOffset() { ASTNode refName = findChildByRole(ChildRole.REFERENCE_NAME); @@ -684,20 +639,6 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements return new TextRange(nameChild.getStartOffsetInParent(), getTextLength()); } - @Override - public PsiElement getElement() { - return this; - } - - @Override - @NotNull - public PsiType[] getTypeParameters() { - final PsiReferenceParameterList parameterList = getParameterList(); - if (parameterList == null) return PsiType.EMPTY_ARRAY; - return parameterList.getTypeArguments(); - } - - @Override public String getClassNameText() { String cachedQName = myCachedQName; @@ -744,4 +685,3 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements return whiteSpaceAndComments; } } - diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MethodReferences.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MethodReferences.java new file mode 100644 index 000000000000..150cf88144fe --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/MethodReferences.java @@ -0,0 +1,26 @@ +/* + * 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. + */ + +class C { + interface Simplest { + void m(); + } + void simplest() { } + + void test() { + Simplest simplest = this::simplest; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef0.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef0.txt new file mode 100644 index 000000000000..28d943ef1c0e --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef0.txt @@ -0,0 +1,20 @@ +PsiJavaFile:MethodRef0.java + PsiMethodReferenceExpression:a.b.C::m + PsiReferenceExpression:a.b.C + PsiReferenceExpression:a.b + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:b('b') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:C('C') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:m('m') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef1.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef1.txt new file mode 100644 index 000000000000..16de47b23d45 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef1.txt @@ -0,0 +1,27 @@ +PsiJavaFile:MethodRef1.java + PsiMethodReferenceExpression:a.b.C::new + PsiTypeElement:a.b.C + PsiJavaCodeReferenceElement:a.b.C + PsiJavaCodeReferenceElement:a.b + PsiJavaCodeReferenceElement:a + PsiIdentifier:a('a') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:b('b') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:C('C') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiKeyword:new('new') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef2.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef2.txt new file mode 100644 index 000000000000..3763aa5e3eba --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef2.txt @@ -0,0 +1,16 @@ +PsiJavaFile:MethodRef2.java + PsiMethodReferenceExpression:C::m + PsiReferenceExpression:C + PsiReferenceParameterList + + PsiIdentifier:C('C') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiIdentifier:m('m') diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef3.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef3.txt new file mode 100644 index 000000000000..f13cc341917b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef3.txt @@ -0,0 +1,17 @@ +PsiJavaFile:MethodRef3.java + PsiMethodReferenceExpression:a[i]::m + PsiArrayAccessExpression:a[i] + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiJavaToken:LBRACKET('[') + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiJavaToken:RBRACKET(']') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:m('m') diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef4.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef4.txt new file mode 100644 index 000000000000..31cf24bdb999 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef4.txt @@ -0,0 +1,11 @@ +PsiJavaFile:MethodRef4.java + PsiMethodReferenceExpression:int[]::clone + PsiTypeElement:int[] + PsiTypeElement:int + PsiKeyword:int('int') + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:clone('clone') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/expressions/MethodRef5.txt b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef5.txt new file mode 100644 index 000000000000..4a82070c8b37 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/MethodRef5.txt @@ -0,0 +1,55 @@ +PsiJavaFile:MethodRef5.java + PsiMethodReferenceExpression:(f ? list.map(String::length) : Collections.emptyList())::iterator + PsiParenthesizedExpression:(f ? list.map(String::length) : Collections.emptyList()) + PsiJavaToken:LPARENTH('(') + PsiConditionalExpression:f ? list.map(String::length) : Collections.emptyList() + PsiReferenceExpression:f + PsiReferenceParameterList + + PsiIdentifier:f('f') + PsiWhiteSpace(' ') + PsiJavaToken:QUEST('?') + PsiWhiteSpace(' ') + PsiMethodCallExpression:list.map(String::length) + PsiReferenceExpression:list.map + PsiReferenceExpression:list + PsiReferenceParameterList + + PsiIdentifier:list('list') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:map('map') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiMethodReferenceExpression:String::length + PsiReferenceExpression:String + PsiReferenceParameterList + + PsiIdentifier:String('String') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:length('length') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiJavaToken:COLON(':') + PsiWhiteSpace(' ') + PsiMethodCallExpression:Collections.emptyList() + PsiReferenceExpression:Collections.emptyList + PsiReferenceExpression:Collections + PsiReferenceParameterList + + PsiIdentifier:Collections('Collections') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:emptyList('emptyList') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:DOUBLE_COLON('::') + PsiReferenceParameterList + + PsiIdentifier:iterator('iterator') diff --git a/java/java-tests/testData/psi/parser-partial/expressions/PrimitiveFieldAccess.txt b/java/java-tests/testData/psi/parser-partial/expressions/PrimitiveFieldAccess.txt new file mode 100644 index 000000000000..13e35abdce09 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/expressions/PrimitiveFieldAccess.txt @@ -0,0 +1,11 @@ +PsiJavaFile:PrimitiveFieldAccess.java + PsiReferenceExpression:int.x + PsiClassObjectAccessExpression:int + PsiTypeElement:int + PsiKeyword:int('int') + PsiErrorElement:.class expected + + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:x('x') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java index aa839c1bae79..ac076e6a6c0f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java @@ -184,4 +184,5 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase { public void testIDEA84533() throws Exception { doTest(false, false); } public void testClassLiteral() throws Exception { doTest(false, false); } public void testExtensionMethods() throws Exception { doTest(false, false); } + public void testMethodReferences() throws Exception { doTest(false, false); } } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java index f43e356abed7..838d0c4fcf2a 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java @@ -111,9 +111,17 @@ public class ExpressionParserTest extends JavaParsingTestCase { public void testQualifiedSuperMethodCall1() { doParserTest("d.super(0)"); } public void testSuperMethodCallTypeParameterList() { doParserTest("super()"); } public void testPrimitiveClassObjectAccess() { doParserTest("int.class"); } + public void testPrimitiveFieldAccess() { doParserTest("int.x"); } public void testChainedClassObjectAccess() { doParserTest("A.class.B.class"); } public void testChainedThisObjectAccess() { doParserTest("A.this.B.this"); } + public void testMethodRef0() { doParserTest("a.b.C::m"); } + public void testMethodRef1() { doParserTest("a.b.C::new"); } + public void testMethodRef2() { doParserTest("C::m"); } + public void testMethodRef3() { doParserTest("a[i]::m"); } + public void testMethodRef4() { doParserTest("int[]::clone"); } + public void testMethodRef5() { doParserTest("(f ? list.map(String::length) : Collections.emptyList())::iterator"); } + private void doParserTest(@NonNls final String text) { doParserTest(text, new MyTestParser()); } diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java index 3bdb5b8f8fe5..70875e35aa49 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/actions/BookmarksAction.java @@ -46,6 +46,8 @@ import java.util.List; public class BookmarksAction extends AnAction implements DumbAware, MasterDetailPopupBuilder.Delegate { + private JBPopup myPopup; + @Override public void update(AnActionEvent e) { DataContext dataContext = e.getDataContext(); @@ -59,6 +61,7 @@ public class BookmarksAction extends AnAction implements DumbAware, MasterDetail final Project project = PlatformDataKeys.PROJECT.getData(dataContext); if (project == null) return; + if (myPopup != null && myPopup.isVisible()) return; final DefaultListModel model = buildModel(project); @@ -72,13 +75,13 @@ public class BookmarksAction extends AnAction implements DumbAware, MasterDetail actions.add(new MoveBookmarkUpAction(project, list)); actions.add(new MoveBookmarkDownAction(project, list)); - final JBPopup popup = new MasterDetailPopupBuilder(project). + myPopup = new MasterDetailPopupBuilder(project). setActionsGroup(actions). setList(list). setDelegate(this).createMasterDetailPopup(); - editDescriptionAction.setPopup(popup); - popup.showCenteredInCurrentWindow(project); + editDescriptionAction.setPopup(myPopup); + myPopup.showCenteredInCurrentWindow(project); } @Override diff --git a/platform/platform-api/src/com/intellij/openapi/fileEditor/OpenFileDescriptor.java b/platform/platform-api/src/com/intellij/openapi/fileEditor/OpenFileDescriptor.java index 8a2837a5dc5d..b4b3d97e4f11 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileEditor/OpenFileDescriptor.java +++ b/platform/platform-api/src/com/intellij/openapi/fileEditor/OpenFileDescriptor.java @@ -126,7 +126,7 @@ public class OpenFileDescriptor implements Navigatable { FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(myFile,project); if (type == null || !myFile.isValid()) return false; - if (type instanceof INativeFileType && requestFocus) { + if (type instanceof INativeFileType) { return ((INativeFileType) type).openFileInAssociatedApplication(project, myFile); } diff --git a/platform/platform-api/src/com/intellij/ui/AutoScrollToSourceHandler.java b/platform/platform-api/src/com/intellij/ui/AutoScrollToSourceHandler.java index e5aba7b777e0..bdf0e1babd30 100644 --- a/platform/platform-api/src/com/intellij/ui/AutoScrollToSourceHandler.java +++ b/platform/platform-api/src/com/intellij/ui/AutoScrollToSourceHandler.java @@ -24,6 +24,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileTypes.FileTypes; +import com.intellij.openapi.fileTypes.INativeFileType; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.vfs.PersistentFSConstants; @@ -179,7 +180,7 @@ public abstract class AutoScrollToSourceHandler { if (vFile != null) { // Attempt to navigate to the virtual file with unknown file type will show a modal dialog // asking to register some file type for this file. This behaviour is undesirable when autoscrolling. - if (vFile.getFileType() == FileTypes.UNKNOWN) return; + if (vFile.getFileType() == FileTypes.UNKNOWN || vFile.getFileType() instanceof INativeFileType) return; //IDEA-84881 Don't autoscroll to very large files if (vFile.getLength() > PersistentFSConstants.getMaxIntellisenseFileSize()) return; diff --git a/platform/platform-api/src/com/intellij/ui/components/JBTabbedPane.java b/platform/platform-api/src/com/intellij/ui/components/JBTabbedPane.java index ab87158535a9..6bf0688812a0 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBTabbedPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBTabbedPane.java @@ -4,14 +4,10 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.util.ui.UIUtil; import javax.swing.*; -import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; -import java.util.Arrays; /** * @author evgeny.zakrevsky diff --git a/platform/platform-api/src/com/intellij/util/ui/AsyncProcessIcon.java b/platform/platform-api/src/com/intellij/util/ui/AsyncProcessIcon.java index 4d89b8fe863a..172955086af2 100644 --- a/platform/platform-api/src/com/intellij/util/ui/AsyncProcessIcon.java +++ b/platform/platform-api/src/com/intellij/util/ui/AsyncProcessIcon.java @@ -91,7 +91,20 @@ public class AsyncProcessIcon extends AnimatedIcon { } public static class Big extends AsyncProcessIcon { - private static final Icon[] BIG_ICONS = findIcons("/process/big/step_", null); + private static final Icon[] BIG_ICONS = { + AllIcons.Process.Big.Step_1, + AllIcons.Process.Big.Step_2, + AllIcons.Process.Big.Step_3, + AllIcons.Process.Big.Step_4, + AllIcons.Process.Big.Step_5, + AllIcons.Process.Big.Step_6, + AllIcons.Process.Big.Step_7, + AllIcons.Process.Big.Step_8, + AllIcons.Process.Big.Step_9, + AllIcons.Process.Big.Step_10, + AllIcons.Process.Big.Step_11, + AllIcons.Process.Big.Step_12 + }; private static final Icon BIG_PASSIVE_ICON = AllIcons.Process.Big.Step_passive; public Big(@NonNls final String name) { diff --git a/platform/platform-api/src/com/intellij/util/ui/RefreshFileSystemIcon.java b/platform/platform-api/src/com/intellij/util/ui/RefreshFileSystemIcon.java index dea652841547..2ef0eea61119 100644 --- a/platform/platform-api/src/com/intellij/util/ui/RefreshFileSystemIcon.java +++ b/platform/platform-api/src/com/intellij/util/ui/RefreshFileSystemIcon.java @@ -15,7 +15,7 @@ */ package com.intellij.util.ui; -import com.intellij.openapi.util.IconLoader; +import com.intellij.icons.AllIcons; import javax.swing.*; import java.awt.*; @@ -24,16 +24,26 @@ import java.awt.*; * @author Konstantin Bulenkov */ public class RefreshFileSystemIcon extends AnimatedIcon { - private static final Icon[] ICONS = getIcons("/process/fs/step_", 18); - - private static Icon[] getIcons(String path, int count) { - Icon[] icons = new Icon[count]; - for (int i = 0; i < icons.length; i++) { - int index = i + 1; - icons[i] = IconLoader.getIcon(path + index + ".png"); - } - return icons; - } + private static final Icon[] ICONS = { + AllIcons.Process.FS.Step_1, + AllIcons.Process.FS.Step_2, + AllIcons.Process.FS.Step_3, + AllIcons.Process.FS.Step_4, + AllIcons.Process.FS.Step_5, + AllIcons.Process.FS.Step_6, + AllIcons.Process.FS.Step_7, + AllIcons.Process.FS.Step_8, + AllIcons.Process.FS.Step_9, + AllIcons.Process.FS.Step_10, + AllIcons.Process.FS.Step_11, + AllIcons.Process.FS.Step_12, + AllIcons.Process.FS.Step_13, + AllIcons.Process.FS.Step_14, + AllIcons.Process.FS.Step_15, + AllIcons.Process.FS.Step_16, + AllIcons.Process.FS.Step_17, + AllIcons.Process.FS.Step_18 + }; public RefreshFileSystemIcon() { super("Refreshing filesystem", ICONS, EmptyIcon.ICON_16, 800); diff --git a/platform/util/src/com/intellij/util/io/DirectBufferWrapper.java b/platform/util/src/com/intellij/util/io/DirectBufferWrapper.java index 00049a0619fa..329d2704239b 100644 --- a/platform/util/src/com/intellij/util/io/DirectBufferWrapper.java +++ b/platform/util/src/com/intellij/util/io/DirectBufferWrapper.java @@ -16,10 +16,15 @@ package com.intellij.util.io; import com.intellij.openapi.diagnostic.Logger; +import org.jetbrains.annotations.Nullable; +import sun.misc.Cleaner; +import sun.nio.ch.DirectBuffer; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; public abstract class DirectBufferWrapper extends ByteBufferWrapper { protected static final Logger LOG = Logger.getInstance("#com.intellij.util.io.DirectBufferWrapper"); @@ -49,6 +54,24 @@ public abstract class DirectBufferWrapper extends ByteBufferWrapper { @Override public void unmap() { if (isDirty()) flush(); + if (myBuffer != null) disposeDirectBuffer((DirectBuffer)myBuffer); myBuffer = null; } + + static boolean disposeDirectBuffer(final DirectBuffer buffer) { + return AccessController.doPrivileged(new PrivilegedAction() { + @Nullable + public Object run() { + try { + Cleaner cleaner = buffer.cleaner(); + if (cleaner != null) cleaner.clean(); // Already cleaned otherwise + return null; + } + catch (Exception e) { + return buffer; + } + } + }) == null; + } + } diff --git a/platform/util/src/com/intellij/util/io/MappedBufferWrapper.java b/platform/util/src/com/intellij/util/io/MappedBufferWrapper.java index 0f86ffa4d433..56ce86c62e7a 100644 --- a/platform/util/src/com/intellij/util/io/MappedBufferWrapper.java +++ b/platform/util/src/com/intellij/util/io/MappedBufferWrapper.java @@ -16,16 +16,12 @@ package com.intellij.util.io; import com.intellij.openapi.diagnostic.Logger; -import org.jetbrains.annotations.Nullable; -import sun.misc.Cleaner; import sun.nio.ch.DirectBuffer; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; -import java.security.AccessController; -import java.security.PrivilegedAction; /** * @author max @@ -81,19 +77,7 @@ public abstract class MappedBufferWrapper extends ByteBufferWrapper { return false; } - return AccessController.doPrivileged(new PrivilegedAction() { - @Nullable - public Object run() { - try { - Cleaner cleaner = ((DirectBuffer)buffer).cleaner(); - if (cleaner != null) cleaner.clean(); // Already cleaned otherwise - return null; - } - catch (Exception e) { - return buffer; - } - } - }) == null; + return DirectBufferWrapper.disposeDirectBuffer((DirectBuffer)buffer); } private static boolean tryForce(MappedByteBuffer buffer) { diff --git a/plugins/IntentionPowerPak/src/META-INF/plugin.xml b/plugins/IntentionPowerPak/src/META-INF/plugin.xml index de6e67a064f7..d52b3c759254 100644 --- a/plugins/IntentionPowerPak/src/META-INF/plugin.xml +++ b/plugins/IntentionPowerPak/src/META-INF/plugin.xml @@ -370,7 +370,7 @@ intention.category.strings - com.siyeh.ipp.concatenation.MakeAppendChainIntoAppendSequenceIntention + com.siyeh.ipp.concatenation.MakeCallChainIntoCallSequenceIntention intention.category.other diff --git a/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties b/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties index 41d59b6dc7a3..86fe12496982 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties +++ b/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties @@ -81,8 +81,8 @@ replace.if.with.conditional.intention.name=Replace 'if else' with '?:' replace.if.with.conditional.intention.family.name=Replace If Else with Conditional replace.equality.with.equals.intention.name=Replace '==' with '.equals()' replace.equality.with.equals.intention.family.name=Replace Equality with Equals -make.append.chain.into.append.sequence.intention.name=Make '.append()' chain into '.append()' sequence -make.append.chain.into.append.sequence.intention.family.name=Make Append Chain Into Append Sequence +make.call.chain.into.call.sequence.intention.name=Make method call chain into method call sequence +make.call.chain.into.call.sequence.intention.family.name=Make Call Chain Into Call Sequence detail.exceptions.intention.name=Detail exceptions detail.exceptions.intention.family.name=Detail Exceptions flip.conditional.intention.name=Flip '?:' diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/AppendChainPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/AppendChainPredicate.java deleted file mode 100644 index 6b04e11b95fe..000000000000 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/AppendChainPredicate.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers - * - * 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.siyeh.ipp.concatenation; - -import com.intellij.psi.*; -import com.siyeh.ipp.base.PsiElementPredicate; - -class AppendChainPredicate implements PsiElementPredicate { - - public boolean satisfiedBy(PsiElement element) { - if (!AppendUtil.isAppendCall(element)) { - return false; - } - final PsiMethodCallExpression call = (PsiMethodCallExpression)element; - final PsiReferenceExpression methodExpression = - call.getMethodExpression(); - final PsiExpression qualifier = - methodExpression.getQualifierExpression(); - if (!(qualifier instanceof PsiMethodCallExpression)) { - return false; - } - final PsiMethodCallExpression qualifierCall = - (PsiMethodCallExpression)element; - if (!AppendUtil.isAppendCall(qualifierCall)) { - return false; - } - final PsiElement parent = element.getParent(); - if (parent instanceof PsiExpressionStatement) { - return true; - } - final PsiElement grandParent = parent.getParent(); - if (parent instanceof PsiLocalVariable && - grandParent instanceof PsiDeclarationStatement) { - final PsiDeclarationStatement declarationStatement = - (PsiDeclarationStatement)grandParent; - if (declarationStatement.getDeclaredElements().length == 1) { - return true; - } - } - return parent instanceof PsiAssignmentExpression && - grandParent instanceof PsiExpressionStatement; - } -} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/AppendUtil.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/AppendUtil.java deleted file mode 100644 index b877f42519ae..000000000000 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/AppendUtil.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers - * - * 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.siyeh.ipp.concatenation; - -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; -import org.jetbrains.annotations.NonNls; - -class AppendUtil { - - private AppendUtil() { - } - - public static boolean isAppendCall(PsiElement element) { - if (!(element instanceof PsiMethodCallExpression)) { - return false; - } - final PsiMethodCallExpression methodCallExpression = - (PsiMethodCallExpression)element; - final PsiReferenceExpression methodExpression = - methodCallExpression.getMethodExpression(); - @NonNls final String callName = methodExpression.getReferenceName(); - if (!"append".equals(callName)) { - return false; - } - final PsiMethod method = methodCallExpression.resolveMethod(); - final PsiClass containingClass; - if (method == null) { - // if the argument has no type because of invalid code - // this uses the qualifier as type, so the conversion too - // append sequence is still applicable - final PsiExpression qualifierExpression = - methodExpression.getQualifierExpression(); - if (qualifierExpression == null) { - return false; - } - final PsiType type = qualifierExpression.getType(); - if (!(type instanceof PsiClassType)) { - return false; - } - final PsiClassType classType = (PsiClassType)type; - containingClass = classType.resolve(); - } - else { - containingClass = method.getContainingClass(); - } - if (containingClass == null) { - return false; - } - final String name = containingClass.getQualifiedName(); - if (CommonClassNames.JAVA_LANG_STRING_BUFFER.equals(name) || - CommonClassNames.JAVA_LANG_STRING_BUILDER.equals(name)) { - return true; - } - final Project project = containingClass.getProject(); - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - final PsiClass appendableClass = - psiFacade.findClass("java.lang.Appendable", - GlobalSearchScope.allScope(project)); - if (appendableClass == null) { - return false; - } - return containingClass.isInheritor(appendableClass, true); - } -} diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/MakeAppendChainIntoAppendSequenceIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/MakeAppendChainIntoAppendSequenceIntention.java deleted file mode 100644 index fa76c78b3327..000000000000 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/MakeAppendChainIntoAppendSequenceIntention.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers - * - * 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.siyeh.ipp.concatenation; - -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.util.IncorrectOperationException; -import com.siyeh.ipp.base.Intention; -import com.siyeh.ipp.base.PsiElementPredicate; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class MakeAppendChainIntoAppendSequenceIntention extends Intention { - - @Override - @NotNull - protected PsiElementPredicate getElementPredicate() { - return new AppendChainPredicate(); - } - - @Override - public void processIntention(PsiElement element) - throws IncorrectOperationException { - final PsiExpression call = (PsiExpression)element; - final List argumentsList = new ArrayList(); - PsiExpression currentCall = call; - while (AppendUtil.isAppendCall(currentCall)) { - final PsiMethodCallExpression methodCallExpression = - (PsiMethodCallExpression)currentCall; - final PsiExpressionList arguments = - methodCallExpression.getArgumentList(); - final String argumentsText = arguments.getText(); - argumentsList.add(argumentsText); - final PsiReferenceExpression methodExpression = - methodCallExpression.getMethodExpression(); - currentCall = methodExpression.getQualifierExpression(); - if (currentCall == null) { - return; - } - } - final String targetText; - final PsiStatement appendStatement; - @NonNls final String firstStatement; - final PsiElement parent = call.getParent(); - if (parent instanceof PsiExpressionStatement) { - targetText = currentCall.getText(); - appendStatement = (PsiStatement)parent; - firstStatement = null; - } - else { - final PsiElement grandParent = parent.getParent(); - appendStatement = (PsiStatement)grandParent; - if (parent instanceof PsiAssignmentExpression && - grandParent instanceof PsiExpressionStatement) { - final PsiAssignmentExpression assignment = - (PsiAssignmentExpression)parent; - final PsiExpression lhs = assignment.getLExpression(); - targetText = lhs.getText(); - final PsiJavaToken token = assignment.getOperationSign(); - firstStatement = targetText + token.getText() + - currentCall.getText() + ';'; - } - else { - final PsiDeclarationStatement declaration = - (PsiDeclarationStatement)appendStatement; - final PsiVariable variable = - (PsiVariable)declaration.getDeclaredElements()[0]; - targetText = variable.getName(); - final PsiType variableType = variable.getType(); - if (variable.hasModifierProperty(PsiModifier.FINAL)) { - firstStatement = - "final " + variableType.getPresentableText() + - ' ' + variable.getName() + '=' + - currentCall.getText() + ';'; - } - else { - firstStatement = variableType.getPresentableText() + - ' ' + variable.getName() + '=' + - currentCall.getText() + ';'; - } - } - } - final StringBuilder builder = new StringBuilder("{"); - if (firstStatement != null) { - builder.append(firstStatement); - } - Collections.reverse(argumentsList); - for (String argument : argumentsList) { - builder.append(targetText); - builder.append(".append"); - builder.append(argument); - builder.append(';'); - } - builder.append('}'); - final PsiManager manager = element.getManager(); - final Project project = manager.getProject(); - final PsiElementFactory factory = - JavaPsiFacade.getElementFactory(project); - final PsiElement appendStatementParent = appendStatement.getParent(); - final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(manager.getProject()); - final PsiCodeBlock codeBlock = - factory.createCodeBlockFromText(builder.toString(), - appendStatement); - if (appendStatementParent instanceof PsiLoopStatement || - appendStatementParent instanceof PsiIfStatement) { - final PsiElement insertedStatement = - appendStatement.replace(codeBlock); - codeStyleManager.reformat(insertedStatement); - } - else { - final PsiStatement[] statements = codeBlock.getStatements(); - for (PsiStatement statement : statements) { - final PsiElement insertedStatement = - appendStatementParent.addBefore(statement, - appendStatement); - codeStyleManager.reformat(insertedStatement); - } - appendStatement.delete(); - } - } -} diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/MakeCallChainIntoCallSequenceIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/MakeCallChainIntoCallSequenceIntention.java new file mode 100644 index 000000000000..24fec03f6f81 --- /dev/null +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/MakeCallChainIntoCallSequenceIntention.java @@ -0,0 +1,184 @@ +/* + * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * + * 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.siyeh.ipp.concatenation; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.ipp.base.Intention; +import com.siyeh.ipp.base.PsiElementPredicate; +import com.siyeh.ipp.psiutils.HighlightUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MakeCallChainIntoCallSequenceIntention extends Intention { + + @Override + @NotNull + protected PsiElementPredicate getElementPredicate() { + return new MethodCallChainPredicate(); + } + + @Override + public void processIntention(@NotNull PsiElement element) throws IncorrectOperationException { + final List callTexts = new ArrayList(); + PsiExpression root = (PsiExpression)element; + while (root instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) root; + final PsiExpressionList arguments = methodCallExpression.getArgumentList(); + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + callTexts.add(methodExpression.getReferenceName() + arguments.getText()); + root = methodExpression.getQualifierExpression(); + if (root == null) { + return; + } + } + final PsiType rootType = root.getType(); + if (rootType == null) { + return; + } + final String targetText; + final PsiStatement appendStatement; + @NonNls final String firstStatement; + final String variableDeclaration; + final boolean showRenameTemplate; + final PsiElement parent = element.getParent(); + if (parent instanceof PsiExpressionStatement) { + targetText = root.getText(); + appendStatement = (PsiStatement)parent; + firstStatement = null; + variableDeclaration = null; + showRenameTemplate = false; + } + else { + final PsiElement grandParent = parent.getParent(); + appendStatement = (PsiStatement)grandParent; + if (parent instanceof PsiAssignmentExpression && grandParent instanceof PsiExpressionStatement) { + final PsiAssignmentExpression assignment = (PsiAssignmentExpression)parent; + final PsiExpression lhs = assignment.getLExpression(); + if (!(lhs instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression expression = (PsiReferenceExpression)lhs; + final PsiElement target = expression.resolve(); + if (!(target instanceof PsiVariable)) { + return; + } + final PsiVariable variable = (PsiVariable)target; + final PsiType variableType = variable.getType(); + if (variableType.equals(rootType)) { + targetText = lhs.getText(); + final PsiJavaToken token = assignment.getOperationSign(); + firstStatement = targetText + token.getText() + root.getText() + ';'; + showRenameTemplate = false; + } else { + targetText = "x"; + showRenameTemplate = true; + final Project project = element.getProject(); + final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project); + if (codeStyleSettings.GENERATE_FINAL_LOCALS) { + firstStatement = "final " + rootType.getCanonicalText() + ' ' + targetText + '=' + root.getText() + ';'; + } else { + firstStatement = rootType.getCanonicalText() + ' ' + targetText + '=' + root.getText() + ';'; + } + } + variableDeclaration = null; + } + else { + final PsiDeclarationStatement declaration = (PsiDeclarationStatement)appendStatement; + final PsiVariable variable = (PsiVariable)declaration.getDeclaredElements()[0]; + final PsiType variableType = variable.getType(); + if (variableType.equals(rootType)) { + targetText = variable.getName(); + if (variable.hasModifierProperty(PsiModifier.FINAL)) { + firstStatement = "final " + variableType.getCanonicalText() + ' ' + variable.getName() + '=' + root.getText() + ';'; + } + else { + firstStatement = variableType.getCanonicalText() + ' ' + variable.getName() + '=' + root.getText() + ';'; + } + variableDeclaration = null; + showRenameTemplate = false; + } else { + if (variable.hasModifierProperty(PsiModifier.FINAL)) { + variableDeclaration = "final " + variableType.getCanonicalText() + ' ' + variable.getName() + '='; + } + else { + variableDeclaration = variableType.getCanonicalText() + ' ' + variable.getName() + '='; + } + targetText = "x"; + showRenameTemplate = true; + final Project project = element.getProject(); + final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project); + if (codeStyleSettings.GENERATE_FINAL_LOCALS) { + firstStatement = "final " + rootType.getCanonicalText() + " x=" + root.getText() + ';'; + } else { + firstStatement = rootType.getCanonicalText() + " x=" + root.getText() + ';'; + } + } + } + } + final StringBuilder builder = new StringBuilder("{\n"); + if (firstStatement != null) { + builder.append(firstStatement); + } + Collections.reverse(callTexts); + for (int i = 0, size = callTexts.size(); i < size; i++) { + final String callText = callTexts.get(i); + if (i == size - 1 && variableDeclaration != null) { + builder.append(variableDeclaration); + } + builder.append(targetText).append('.').append(callText).append(";\n"); + } + builder.append('}'); + final PsiManager manager = element.getManager(); + final Project project = manager.getProject(); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiElement appendStatementParent = appendStatement.getParent(); + final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(manager.getProject()); + final PsiCodeBlock codeBlock = factory.createCodeBlockFromText(builder.toString(), appendStatement); + if (appendStatementParent instanceof PsiLoopStatement || appendStatementParent instanceof PsiIfStatement) { + final PsiElement insertedCodeBlock = appendStatement.replace(codeBlock); + final PsiCodeBlock reformattedCodeBlock = (PsiCodeBlock)codeStyleManager.reformat(insertedCodeBlock); + if (showRenameTemplate) { + final PsiStatement[] statements = reformattedCodeBlock.getStatements(); + final PsiVariable variable = (PsiVariable)((PsiDeclarationStatement) statements[0]).getDeclaredElements()[0]; + HighlightUtil.showRenameTemplate(appendStatementParent, variable); + } + } + else { + final PsiStatement[] statements = codeBlock.getStatements(); + PsiVariable variable = null; + for (int i = 0, length = statements.length; i < length; i++) { + final PsiElement insertedStatement = appendStatementParent.addBefore(statements[i], appendStatement); + if (i == 0 && showRenameTemplate) { + variable = (PsiVariable)((PsiDeclarationStatement) insertedStatement).getDeclaredElements()[0]; + } + codeStyleManager.reformat(insertedStatement); + } + appendStatement.delete(); + if (variable != null) { + HighlightUtil.showRenameTemplate(appendStatementParent, variable); + } + } + } +} diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/MethodCallChainPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/MethodCallChainPredicate.java new file mode 100644 index 000000000000..57eb68d61e49 --- /dev/null +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/MethodCallChainPredicate.java @@ -0,0 +1,96 @@ +/* + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * + * 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.siyeh.ipp.concatenation; + +import com.intellij.psi.*; +import com.siyeh.ipp.base.PsiElementPredicate; +import org.jetbrains.annotations.Nullable; + +class MethodCallChainPredicate implements PsiElementPredicate { + + public boolean satisfiedBy(PsiElement element) { + if (!isCallChain(element)) { + return false; + } + final PsiElement parent = element.getParent(); + if (parent instanceof PsiExpressionStatement) { + return true; + } + else if (parent instanceof PsiLocalVariable) { + final PsiElement grandParent = parent.getParent(); + if (!(grandParent instanceof PsiDeclarationStatement)) { + return false; + } + final PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement)grandParent; + return declarationStatement.getDeclaredElements().length == 1; + } + else if (parent instanceof PsiAssignmentExpression) { + final PsiElement grandParent = parent.getParent(); + return grandParent instanceof PsiExpressionStatement; + } + return false; + } + + private static boolean isCallChain(PsiElement element) { + PsiClass aClass1 = getQualifierExpressionType(element); + if (aClass1 == null) { + return false; + } + boolean first = true; + while (aClass1 != null) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + PsiClass aClass2 = getQualifierExpressionType(qualifierExpression); + if (!first) { + if (aClass2 == null) { + return true; + } + } else { + first = false; + } + if (!aClass1.equals(aClass2)) { + return false; + } + aClass1 = aClass2; + element = qualifierExpression; + } + return true; + } + + @Nullable + private static PsiClass getQualifierExpressionType(PsiElement element) { + if (!(element instanceof PsiMethodCallExpression)) { + return null; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element; + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method != null) { + return method.getContainingClass(); + } + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + if (!(qualifierExpression instanceof PsiMethodCallExpression)) { + return null; + } + final PsiType type = qualifierExpression.getType(); + if (!(type instanceof PsiClassType)) { + return null; + } + final PsiClassType classType = (PsiClassType)type; + return classType.resolve(); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/HighlightUtil.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/HighlightUtil.java index e03c5c876868..8260230f4d4c 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/HighlightUtil.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/HighlightUtil.java @@ -15,7 +15,14 @@ */ package com.siyeh.ipp.psiutils; +import com.intellij.codeInsight.CodeInsightUtilBase; import com.intellij.codeInsight.highlighting.HighlightManager; +import com.intellij.codeInsight.template.Expression; +import com.intellij.codeInsight.template.Template; +import com.intellij.codeInsight.template.TemplateBuilderImpl; +import com.intellij.codeInsight.template.TemplateManager; +import com.intellij.codeInsight.template.impl.MacroCallNode; +import com.intellij.codeInsight.template.macro.SuggestVariableNameMacro; import com.intellij.find.FindManager; import com.intellij.find.FindModel; import com.intellij.openapi.application.Application; @@ -27,11 +34,16 @@ import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.wm.StatusBar; import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiNameIdentifierOwner; +import com.intellij.psi.PsiReference; import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiUtilCore; +import com.intellij.util.Query; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -118,4 +130,32 @@ public class HighlightUtil { } return builder; } + + public static void showRenameTemplate(PsiElement context, PsiNameIdentifierOwner element) { + context = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(context); + final Query query = ReferencesSearch.search(element, element.getUseScope()); + final Collection references = query.findAll(); + final Project project = context.getProject(); + final FileEditorManager fileEditorManager = + FileEditorManager.getInstance(project); + final Editor editor = fileEditorManager.getSelectedTextEditor(); + if (editor == null) { + return; + } + final TemplateBuilderImpl builder = new TemplateBuilderImpl(context); + final Expression macroCallNode = new MacroCallNode( + new SuggestVariableNameMacro()); + final PsiElement identifier = element.getNameIdentifier(); + builder.replaceElement(identifier, "PATTERN", macroCallNode, true); + for (PsiReference reference : references) { + builder.replaceElement(reference, "PATTERN", "PATTERN", false); + } + final Template template = builder.buildInlineTemplate(); + final TextRange textRange = context.getTextRange(); + final int startOffset = textRange.getStartOffset(); + editor.getCaretModel().moveToOffset(startOffset); + final TemplateManager templateManager = + TemplateManager.getInstance(project); + templateManager.startTemplate(editor, template); + } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/intentionDescriptions/MakeAppendChainIntoAppendSequenceIntention/description.html b/plugins/IntentionPowerPak/src/intentionDescriptions/MakeAppendChainIntoAppendSequenceIntention/description.html deleted file mode 100644 index 48cc44a99b41..000000000000 --- a/plugins/IntentionPowerPak/src/intentionDescriptions/MakeAppendChainIntoAppendSequenceIntention/description.html +++ /dev/null @@ -1,6 +0,0 @@ - - -This intention replaces chains of StringBuffer.append() calls with -the equivalent sequence of call statements. - - diff --git a/plugins/IntentionPowerPak/src/intentionDescriptions/MakeAppendChainIntoAppendSequenceIntention/after.java.template b/plugins/IntentionPowerPak/src/intentionDescriptions/MakeCallChainIntoCallSequenceIntention/after.java.template similarity index 100% rename from plugins/IntentionPowerPak/src/intentionDescriptions/MakeAppendChainIntoAppendSequenceIntention/after.java.template rename to plugins/IntentionPowerPak/src/intentionDescriptions/MakeCallChainIntoCallSequenceIntention/after.java.template diff --git a/plugins/IntentionPowerPak/src/intentionDescriptions/MakeAppendChainIntoAppendSequenceIntention/before.java.template b/plugins/IntentionPowerPak/src/intentionDescriptions/MakeCallChainIntoCallSequenceIntention/before.java.template similarity index 100% rename from plugins/IntentionPowerPak/src/intentionDescriptions/MakeAppendChainIntoAppendSequenceIntention/before.java.template rename to plugins/IntentionPowerPak/src/intentionDescriptions/MakeCallChainIntoCallSequenceIntention/before.java.template diff --git a/plugins/IntentionPowerPak/src/intentionDescriptions/MakeCallChainIntoCallSequenceIntention/description.html b/plugins/IntentionPowerPak/src/intentionDescriptions/MakeCallChainIntoCallSequenceIntention/description.html new file mode 100644 index 000000000000..7d6edba57bfd --- /dev/null +++ b/plugins/IntentionPowerPak/src/intentionDescriptions/MakeCallChainIntoCallSequenceIntention/description.html @@ -0,0 +1,5 @@ + + +This intention replaces chains of method calls with the equivalent sequence of call statements. + +