Platform support for AspectJ plugin

This commit is contained in:
Roman Shevchenko
2010-02-27 17:42:50 +03:00
parent 7b2030dffc
commit aad20b1026
18 changed files with 273 additions and 25 deletions
@@ -26,6 +26,7 @@ import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.impl.InheritanceImplUtil;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.impl.PsiImplUtil;
@@ -50,6 +51,7 @@ import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -300,12 +302,16 @@ public class PsiClassImpl extends JavaStubPsiElement<PsiClassStub<?>> implements
@NotNull
public PsiField[] getFields() {
return getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY);
final PsiField[] owns = getStubOrPsiChildren(Constants.FIELD_BIT_SET, PsiField.ARRAY_FACTORY);
final List<PsiField> augments = PsiAugmentProvider.collectAugments(this, PsiField.class);
return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiField.ARRAY_FACTORY);
}
@NotNull
public PsiMethod[] getMethods() {
return getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY);
final PsiMethod[] owns = getStubOrPsiChildren(Constants.METHOD_BIT_SET, PsiMethod.ARRAY_FACTORY);
final List<PsiMethod> augments = PsiAugmentProvider.collectAugments(this, PsiMethod.class);
return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiMethod.ARRAY_FACTORY);
}
@NotNull
@@ -93,7 +93,7 @@ public class PsiFieldImpl extends JavaStubPsiElement<PsiFieldStub> implements Ps
}
@NotNull
public final PsiIdentifier getNameIdentifier(){
public PsiIdentifier getNameIdentifier() {
return (PsiIdentifier)getNode().findChildByRoleAsPsiElement(ChildRole.NAME);
}
@@ -545,7 +545,8 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
final boolean preserveQualification = CodeStyleSettingsManager.getSettings(getProject()).USE_FQ_CLASS_NAMES && isFullyQualified();
final PsiManager manager = aClass.getManager();
String text = qName + getParameterList().getText();
final PsiReferenceParameterList parameterList = getParameterList();
String text = (parameterList != null ? qName + parameterList.getText() : qName);
ASTNode ref = Parsing.parseJavaCodeReferenceText(manager, text, SharedImplUtil.findCharTableByTree(this));
LOG.assertTrue(ref != null, "Failed to parse reference from text '" + text + "'");
getTreeParent().replaceChildInternal(this, (TreeElement)ref);
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.daemon.impl.analysis.AnnotationsHighlightUtil;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.impl.CheckUtil;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.cache.ModifierFlags;
@@ -29,6 +30,7 @@ import com.intellij.psi.impl.source.tree.Factory;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashMap;
@@ -254,7 +256,9 @@ public class PsiModifierListImpl extends JavaStubPsiElement<PsiModifierListStub>
@NotNull
public PsiAnnotation[] getAnnotations() {
return getStubOrPsiChildren(JavaStubElementTypes.ANNOTATION, PsiAnnotation.ARRAY_FACTORY);
final PsiAnnotation[] owns = getStubOrPsiChildren(JavaStubElementTypes.ANNOTATION, PsiAnnotation.ARRAY_FACTORY);
final List<PsiAnnotation> augments = PsiAugmentProvider.collectAugments(this, PsiAnnotation.class);
return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiAnnotation.ARRAY_FACTORY);
}
@NotNull
@@ -18,13 +18,17 @@ package com.intellij.psi.impl.source;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.impl.java.stubs.PsiClassReferenceListStub;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public final class PsiReferenceListImpl extends JavaStubPsiElement<PsiClassReferenceListStub> implements PsiReferenceList {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiReferenceListImpl");
private static final TokenSet REFERENCE_BIT_SET = TokenSet.create(Constants.JAVA_CODE_REFERENCE);
@@ -39,7 +43,10 @@ public final class PsiReferenceListImpl extends JavaStubPsiElement<PsiClassRefer
@NotNull
public PsiJavaCodeReferenceElement[] getReferenceElements() {
return calcTreeElement().getChildrenAsPsiElements(REFERENCE_BIT_SET, Constants.PSI_REFERENCE_ELEMENT_ARRAY_CONSTRUCTOR);
final PsiJavaCodeReferenceElement[] owns =
calcTreeElement().getChildrenAsPsiElements(REFERENCE_BIT_SET, Constants.PSI_REFERENCE_ELEMENT_ARRAY_CONSTRUCTOR);
final List<PsiJavaCodeReferenceElement> augments = PsiAugmentProvider.collectAugments(this, PsiJavaCodeReferenceElement.class);
return ArrayUtil.mergeArrayAndCollection(owns, augments, PsiJavaCodeReferenceElement.ARRAY_FACTORY);
}
@NotNull
@@ -469,6 +469,20 @@ public class DeclarationParsing extends Parsing {
return first;
}
@NotNull
public CompositeElement parseAnnotationParamsFromText(PsiManager manager, CharSequence text, final LanguageLevel languageLevel) {
Lexer originalLexer = new JavaLexer(languageLevel);
FilterLexer lexer = new FilterLexer(originalLexer, new FilterLexer.SetFilter(StdTokenSets.WHITE_SPACE_OR_COMMENT_BIT_SET));
lexer.start(text);
CompositeElement first = parseAnnotationParameterList(lexer);
final FileElement dummyRoot = DummyHolderFactory.createHolder(manager, null, myContext.getCharTable()).getTreeElement();
dummyRoot.rawAddChildren(first);
ParseUtil.insertMissingTokens(dummyRoot, originalLexer, 0, text.length(), -1, WhiteSpaceAndCommentsProcessor.INSTANCE, myContext);
return first;
}
@NotNull
CompositeElement parseAnnotation(Lexer lexer) {
CompositeElement annotation = ASTFactory.composite(JavaElementType.ANNOTATION);
@@ -24,9 +24,8 @@ import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
import com.intellij.psi.impl.java.stubs.PsiAnnotationStub;
import com.intellij.psi.impl.meta.MetaRegistry;
import com.intellij.psi.impl.source.JavaStubPsiElement;
import com.intellij.psi.impl.source.tree.ChildRole;
import com.intellij.psi.impl.source.tree.CompositeElement;
import com.intellij.psi.meta.PsiMetaData;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.PairFunction;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -51,17 +50,7 @@ public class PsiAnnotationImpl extends JavaStubPsiElement<PsiAnnotationStub> imp
}
public PsiJavaCodeReferenceElement getNameReferenceElement() {
return (PsiJavaCodeReferenceElement)getMirrorTreeElement().findChildByRoleAsPsiElement(ChildRole.CLASS_REFERENCE);
}
private CompositeElement getMirrorTreeElement() {
final PsiAnnotationStub stub = getStub();
if (stub != null) {
return stub.getTreeElement();
}
return (CompositeElement)getNode();
return PsiTreeUtil.getChildOfType(this, PsiJavaCodeReferenceElement.class);
}
public PsiAnnotationMemberValue findAttributeValue(String attributeName) {
@@ -83,7 +72,7 @@ public class PsiAnnotationImpl extends JavaStubPsiElement<PsiAnnotationStub> imp
@NotNull
public PsiAnnotationParameterList getParameterList() {
return (PsiAnnotationParameterList)getMirrorTreeElement().findChildByRoleAsPsiElement(ChildRole.PARAMETER_LIST);
return PsiTreeUtil.getRequiredChildOfType(this, PsiAnnotationParameterList.class);
}
@Nullable public String getQualifiedName() {
@@ -126,8 +126,8 @@ public class PsiNameValuePairImpl extends CompositePsiElement implements PsiName
public PsiReference getReference() {
return new PsiReference() {
private PsiClass getReferencedClass () {
LOG.assertTrue(getTreeParent().getElementType() == ANNOTATION_PARAMETER_LIST && getTreeParent().getTreeParent().getElementType() == ANNOTATION);
PsiAnnotationImpl annotation = (PsiAnnotationImpl)getTreeParent().getTreeParent().getPsi();
LOG.assertTrue(getParent() instanceof PsiAnnotationParameterList && getParent().getParent() instanceof PsiAnnotation);
PsiAnnotation annotation = (PsiAnnotation)getParent().getParent();
PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
return nameRef == null ? null : (PsiClass)nameRef.resolve();
}
@@ -15,6 +15,7 @@
*/
package com.intellij.psi;
import com.intellij.util.ArrayFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -28,6 +29,12 @@ public interface PsiJavaCodeReferenceElement extends PsiJavaReference, PsiQualif
*/
PsiJavaCodeReferenceElement[] EMPTY_ARRAY = new PsiJavaCodeReferenceElement[0];
ArrayFactory<PsiJavaCodeReferenceElement> ARRAY_FACTORY = new ArrayFactory<PsiJavaCodeReferenceElement>() {
public PsiJavaCodeReferenceElement[] create(int count) {
return count == 0 ? EMPTY_ARRAY : new PsiJavaCodeReferenceElement[count];
}
};
/**
* Returns the element representing the name of the referenced element.
*
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2010 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.augment;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public abstract class PsiAugmentProvider {
public static final ExtensionPointName<PsiAugmentProvider> EP_NAME = ExtensionPointName.create("com.intellij.lang.psiAugmentProvider");
private static final PsiAugmentProvider[] PROVIDERS = Extensions.getExtensions(EP_NAME);
@NotNull
public abstract <Psi extends PsiElement> List<Psi> getAugments(@NotNull PsiElement element, @NotNull Class<Psi> type);
@NotNull
public static <Psi extends PsiElement> List<Psi> collectAugments(@NotNull final PsiElement element, @NotNull final Class<Psi> type) {
final List<Psi> augments = new ArrayList<Psi>();
for (PsiAugmentProvider provider : PROVIDERS) {
final List<Psi> list = provider.getAugments(element, type);
augments.addAll(list);
}
return augments;
}
}
@@ -19,9 +19,13 @@ package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.ExternallyDefinedPsiElement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -58,11 +62,17 @@ public class ProblemsHolder {
}
public void registerProblem(@NotNull ProblemDescriptor problemDescriptor) {
if (myProblems == null) {
myProblems = new ArrayList<ProblemDescriptor>(1);
}
PsiElement element = problemDescriptor.getPsiElement();
if (element != null && !isInPsiFile(element)) {
ExternallyDefinedPsiElement external = PsiTreeUtil.getParentOfType(element, ExternallyDefinedPsiElement.class, false);
if (external != null) {
PsiElement newTarget = external.getProblemTarget();
if (newTarget != null) {
redirectProblem(problemDescriptor, newTarget);
return;
}
}
PsiFile containingFile = element.getContainingFile();
PsiElement context = containingFile.getContext();
PsiElement myContext = myFile.getContext();
@@ -71,6 +81,10 @@ public class ProblemsHolder {
+"Inspection invoked for file: "+ myFile +"; context: "+(myContext == null ? null : myContext.getContainingFile())+"\n"
);
}
if (myProblems == null) {
myProblems = new ArrayList<ProblemDescriptor>(1);
}
myProblems.add(problemDescriptor);
}
@@ -79,6 +93,31 @@ public class ProblemsHolder {
return ArrayUtil.indexOf(myFile.getPsiRoots(), file) != -1;
}
private void redirectProblem(@NotNull final ProblemDescriptor problem, @NotNull final PsiElement target) {
final PsiElement original = problem.getPsiElement();
final VirtualFile vFile = original.getContainingFile().getVirtualFile();
assert vFile != null;
final String path = FileUtil.toSystemIndependentName(vFile.getPath());
String description = problem.getDescriptionTemplate();
if (description.startsWith("<html>")) {
description = description.replace("<html>", "").replace("</html>", "");
}
if (description.startsWith("<body>")) {
description = description.replace("<body>", "").replace("</body>", "");
}
final String template =
InspectionsBundle.message("inspection.redirect.template",
description, path, original.getTextRange().getStartOffset(), vFile.getName());
final InspectionManager manager = InspectionManager.getInstance(original.getProject());
final ProblemDescriptor newProblem =
manager.createProblemDescriptor(target, template, (LocalQuickFix)null, problem.getHighlightType(), isOnTheFly());
registerProblem(newProblem);
}
public void registerProblem(@NotNull PsiReference reference, String descriptionTemplate, ProblemHighlightType highlightType) {
LocalQuickFix[] fixes = null;
if (reference instanceof LocalQuickFixProvider) {
@@ -0,0 +1,36 @@
/*
* Copyright 2000-2010 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;
/**
* Interface for PSI elements which may be injected into other elements but physically belongs
* to other file - like AspectJ inter-type fields/methods.
*/
public interface ExternallyDefinedPsiElement extends PsiElement, SyntheticElement {
/**
* If inspection started for files with injections founds any problem in them (or their child)
* it should be able to display them locally. This method allows to define such substitution element.
* E.g. it may be a class name identifier element for fields/methods injected in that class.<br/>
* See <code>ProblemsHolder.redirectProblem()</code> for details.
*
* @return PSI element to which problem descriptions should be redirected
*/
@Nullable
PsiElement getProblemTarget();
}
@@ -172,6 +172,13 @@ public class PsiTreeUtil {
}
return null;
}
@NotNull public static <T extends PsiElement> T getRequiredChildOfType(@NotNull PsiElement element, @NotNull Class<T> aClass) {
final T child = getChildOfType(element, aClass);
assert child != null: "Missing required child of type " + aClass.getName();
return child;
}
@Nullable public static <T extends PsiElement> T[] getChildrenOfType(@NotNull PsiElement element, @NotNull Class<T> aClass) {
List<T> result = null;
for(PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()){
@@ -0,0 +1,62 @@
/*
* Copyright 2000-2010 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.hint;
import com.intellij.codeInsight.highlighting.TooltipLinkHandler;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* Handles tooltip links in format <code>#navigation/file path:offset</code>.
*/
public class NavigationLinkHandler extends TooltipLinkHandler {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.hint.NavigationLinkHandler");
@Override
public void handleLink(@NotNull final String suffix, @NotNull final Editor editor, @NotNull final JEditorPane tooltip) {
final int pos = suffix.lastIndexOf(':');
if (pos <= 0 || pos == suffix.length()-1) {
LOG.error("Malformed suffix: " + suffix);
return;
}
final String path = suffix.substring(0, pos);
final VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(path);
if (vFile == null) {
LOG.error("Unknown file: " + path);
return;
}
final int offset;
try {
offset = Integer.parseInt(suffix.substring(pos+1));
}
catch (NumberFormatException e) {
LOG.error("Malformed suffix: " + suffix);
return;
}
tooltip.setVisible(false);
new OpenFileDescriptor(editor.getProject(), vFile, offset).navigate(true);
}
}
@@ -617,3 +617,5 @@ inspection.application.chosen.profile.log\ message=Inspecting with profile ''{0}
detach.library.quickfix.name=Detach library
detach.library.roots.quickfix.name=Detach unused library roots
inspection.javadoc.problem.pointing.to.itself=Javadoc pointing to itself
inspection.redirect.template=<html><body>Injected element has problem: {0} (in <a href=\"#navigation/{1}:{2}\">{3}</a>). </body></html>
@@ -115,6 +115,8 @@
<extensionPoint name="lang.treePatcher" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
<extensionPoint name="lang.tokenSeparatorGenerator" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
<extensionPoint name="lang.psiAugmentProvider" interface="com.intellij.psi.augment.PsiAugmentProvider"/>
<extensionPoint name="lang.fileViewProviderFactory" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
<extensionPoint name="fileType.fileViewProviderFactory" beanClass="com.intellij.openapi.fileTypes.FileTypeExtensionPoint"/>
<extensionPoint name="multiLangCommenter"
@@ -395,6 +395,7 @@
<enterHandlerDelegate implementation="com.intellij.codeInsight.editorActions.enter.EnterBetweenBracesHandler" id="EnterBetweenBracesHandler"/>
<codeInsight.linkHandler prefix="#inspection/" handlerClass="com.intellij.codeInsight.hint.InspectionDescriptionLinkHandler"/>
<codeInsight.linkHandler prefix="#navigation/" handlerClass="com.intellij.codeInsight.hint.NavigationLinkHandler"/>
<codeFoldingOptionsProvider implementation="com.intellij.application.options.editor.BaseCodeFoldingOptionsProvider" order="first"/>
<editorOptionsProvider implementation="com.intellij.application.options.editor.EditorSmartKeysConfigurable"/>
@@ -155,6 +155,32 @@ public class ArrayUtil {
return a;
}
/**
* Allocates new array of size <code>array.length + collection.size()</code> and copies elements of <code>array</code> and
* <code>collection</code> to it.
* @param array source array
* @param collection source collection
* @param factory array factory used to create destination array of type <code>T</code>
* @return destination array
*/
@NotNull
public static <T> T[] mergeArrayAndCollection(@NotNull T[] array, @NotNull Collection<T> collection,
@NotNull final ArrayFactory<T> factory) {
if (collection.size() == 0) {
return array;
}
final T[] array2 = collection.toArray(factory.create(collection.size()));
if (array.length == 0) {
return array2;
}
final T[] result = factory.create(array.length + collection.size());
System.arraycopy(array, 0, result, 0, array.length);
System.arraycopy(array2, 0, result, array.length, array2.length);
return result;
}
/**
* Appends <code>element</code> to the <code>src</code> array. As you can
* imagine the appended element will be the last one in the returned result.