blob: d71b54a540f67ca832afc30fe78c1b3be058ea03 [file] [log] [blame]
/*
* Copyright 2000-2014 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 org.jetbrains.idea.devkit.navigation;
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder;
import com.intellij.icons.AllIcons;
import com.intellij.navigation.GotoRelatedItem;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.NotNullFunction;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.util.ExtensionPointCandidate;
import org.jetbrains.idea.devkit.util.ExtensionPointLocator;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class ExtensionPointDeclarationRelatedItemLineMarkerProvider extends DevkitRelatedLineMarkerProviderBase {
private static final NotNullFunction<ExtensionPointCandidate, Collection<? extends PsiElement>> CONVERTER =
new NotNullFunction<ExtensionPointCandidate, Collection<? extends PsiElement>>() {
@NotNull
@Override
public Collection<? extends PsiElement> fun(ExtensionPointCandidate candidate) {
return Collections.singleton(candidate.pointer.getElement());
}
};
private static final NotNullFunction<ExtensionPointCandidate, Collection<? extends GotoRelatedItem>> RELATED_ITEM_PROVIDER =
new NotNullFunction<ExtensionPointCandidate, Collection<? extends GotoRelatedItem>>() {
@NotNull
@Override
public Collection<? extends GotoRelatedItem> fun(ExtensionPointCandidate candidate) {
return GotoRelatedItem.createItems(Collections.singleton(candidate.pointer.getElement()), "DevKit");
}
};
@Override
protected void collectNavigationMarkers(@NotNull PsiElement element, Collection<? super RelatedItemLineMarkerInfo> result) {
if (element instanceof PsiField) {
process((PsiField)element, result);
}
}
private static void process(PsiField psiField, Collection<? super RelatedItemLineMarkerInfo> result) {
if (!isExtensionPointNameDeclarationField(psiField)) return;
final PsiClass epClass = resolveExtensionPointClass(psiField);
if (epClass == null) return;
final String epName = resolveEpName(psiField);
if (epName == null) return;
ExtensionPointLocator locator = new ExtensionPointLocator(epClass);
List<ExtensionPointCandidate> targets =
ContainerUtil.filter(locator.findDirectCandidates(), new Condition<ExtensionPointCandidate>() {
@Override
public boolean value(ExtensionPointCandidate candidate) {
return epName.equals(candidate.epName);
}
});
final RelatedItemLineMarkerInfo<PsiElement> info = NavigationGutterIconBuilder
.create(AllIcons.Nodes.Plugin, CONVERTER, RELATED_ITEM_PROVIDER)
.setTargets(targets)
.setPopupTitle("Choose Extension Point")
.setTooltipText("Extension Point Declaration")
.setAlignment(GutterIconRenderer.Alignment.RIGHT)
.createLineMarkerInfo(psiField.getNameIdentifier());
result.add(info);
}
@Nullable
private static PsiClass resolveExtensionPointClass(PsiField psiField) {
final PsiType typeParameter = PsiUtil.substituteTypeParameter(psiField.getType(),
ExtensionPointName.class.getName(),
0, false);
return PsiUtil.resolveClassInClassTypeOnly(typeParameter);
}
private static String resolveEpName(PsiField psiField) {
final PsiExpression initializer = psiField.getInitializer();
PsiExpressionList expressionList = null;
if (initializer instanceof PsiMethodCallExpression) {
expressionList = ((PsiMethodCallExpression)initializer).getArgumentList();
}
else if (initializer instanceof PsiNewExpression) {
expressionList = ((PsiNewExpression)initializer).getArgumentList();
}
if (expressionList == null) return null;
final PsiExpression[] expressions = expressionList.getExpressions();
if (expressions.length != 1) return null;
final PsiExpression epNameExpression = expressions[0];
final PsiConstantEvaluationHelper helper = JavaPsiFacade.getInstance(psiField.getProject()).getConstantEvaluationHelper();
final Object o = helper.computeConstantExpression(epNameExpression);
return o instanceof String ? (String)o : null;
}
private static boolean isExtensionPointNameDeclarationField(PsiField psiField) {
// *do* allow non-public
if (!psiField.hasModifierProperty(PsiModifier.FINAL) ||
!psiField.hasModifierProperty(PsiModifier.STATIC) ||
psiField.hasModifierProperty(PsiModifier.ABSTRACT)) {
return false;
}
if (!psiField.hasInitializer()) {
return false;
}
final PsiExpression initializer = psiField.getInitializer();
if (!(initializer instanceof PsiMethodCallExpression) &&
!(initializer instanceof PsiNewExpression)) {
return false;
}
final PsiClass fieldClass = PsiTypesUtil.getPsiClass(psiField.getType());
if (fieldClass == null) {
return false;
}
return ExtensionPointName.class.getName().equals(fieldClass.getQualifiedName());
}
}