blob: 432891d9f4def26b63e4a631afd62105cc33951d [file] [log] [blame]
/*
* Copyright 2000-2013 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.plugins.javaFX.fxml.codeInsight;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.icons.AllIcons;
import com.intellij.navigation.GotoRelatedItem;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.javaFX.JavaFxControllerClassIndex;
import org.jetbrains.plugins.javaFX.fxml.FxmlConstants;
import org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory;
import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* User: anna
*/
public class JavaFxRelatedItemLineMarkerProvider extends RelatedItemLineMarkerProvider {
private static final Logger LOG = Logger.getInstance("#" + JavaFxRelatedItemLineMarkerProvider.class.getName());
@Override
protected void collectNavigationMarkers(@NotNull PsiElement element, final Collection<? super RelatedItemLineMarkerInfo> result) {
if (element instanceof PsiField) {
final PsiField field = (PsiField)element;
if (JavaFxPsiUtil.isVisibleInFxml(field) && !field.hasModifierProperty(PsiModifier.STATIC) && !field.hasModifierProperty(PsiModifier.FINAL)) {
final PsiClass containingClass = field.getContainingClass();
if (containingClass != null && containingClass.hasModifierProperty(PsiModifier.PUBLIC) && containingClass.getQualifiedName() != null) {
final PsiMethod[] constructors = containingClass.getConstructors();
boolean defaultConstructor = constructors.length == 0;
for (PsiMethod constructor : constructors) {
if (constructor.getParameterList().getParametersCount() == 0) {
defaultConstructor = true;
break;
}
}
if (!defaultConstructor) return;
final ArrayList<GotoRelatedItem> targets = new ArrayList<GotoRelatedItem>();
collectTargets(field, targets, new Function<PsiElement, GotoRelatedItem>() {
@Override
public GotoRelatedItem fun(PsiElement element) {
return new GotoRelatedItem(element);
}
}, true);
if (targets.isEmpty()) return;
result.add(new RelatedItemLineMarkerInfo<PsiField>(field, field.getNameIdentifier().getTextRange(),
AllIcons.FileTypes.Xml, Pass.UPDATE_OVERRIDEN_MARKERS, null,
new JavaFXIdIconNavigationHandler(), GutterIconRenderer.Alignment.LEFT,
targets));
}
}
}
}
private static <T> void collectTargets(PsiField field, final ArrayList<T> targets, final Function<PsiElement, T> fun, final boolean stopAtFirst) {
final PsiClass containingClass = field.getContainingClass();
LOG.assertTrue(containingClass != null);
final String qualifiedName = containingClass.getQualifiedName();
LOG.assertTrue(qualifiedName != null);
final List<VirtualFile> fxmls = JavaFxControllerClassIndex.findFxmlsWithController(field.getProject(), qualifiedName);
if (fxmls.isEmpty()) return;
ReferencesSearch.search(field, GlobalSearchScope.filesScope(field.getProject(), fxmls)).forEach(
new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
final PsiElement referenceElement = reference.getElement();
if (referenceElement == null) return true;
final PsiFile containingFile = referenceElement.getContainingFile();
if (containingFile == null) return true;
if (!JavaFxFileTypeFactory.isFxml(containingFile)) return true;
if (!(referenceElement instanceof XmlAttributeValue)) return true;
final XmlAttributeValue attributeValue = (XmlAttributeValue)referenceElement;
final PsiElement parent = attributeValue.getParent();
if (!(parent instanceof XmlAttribute)) return true;
if (!FxmlConstants.FX_ID.equals(((XmlAttribute)parent).getName())) return true;
targets.add(fun.fun(parent));
return !stopAtFirst;
}
});
}
private static class JavaFXIdIconNavigationHandler implements GutterIconNavigationHandler<PsiField> {
@Override
public void navigate(MouseEvent e, PsiField field) {
final ArrayList<PsiElement> relatedItems = new ArrayList<PsiElement>();
collectTargets(field, relatedItems, Function.ID, false);
if (relatedItems.size() == 1) {
NavigationUtil.activateFileWithPsiElement(relatedItems.get(0));
return;
}
final JBPopup popup = NavigationUtil
.getPsiElementPopup(relatedItems.toArray(new PsiElement[relatedItems.size()]), "<html>Choose component with fx:id <b>" + field.getName() + "<b></html>");
popup.show(new RelativePoint(e));
}
}
}