package com.squareup.ideaplugin.dagger.handler; import com.intellij.codeInsight.daemon.GutterIconNavigationHandler; import com.intellij.pom.Navigatable; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClassType; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiParameter; import com.intellij.psi.PsiType; import com.intellij.psi.PsiTypeElement; import com.intellij.psi.util.PsiUtilBase; import com.intellij.ui.awt.RelativePoint; import com.squareup.ideaplugin.dagger.Decider; import com.squareup.ideaplugin.dagger.PickTypeAction; import com.squareup.ideaplugin.dagger.PsiConsultantImpl; import com.squareup.ideaplugin.dagger.ShowUsagesAction; import java.awt.event.MouseEvent; import java.util.List; import java.util.concurrent.ExecutionException; import static com.squareup.ideaplugin.dagger.DaggerConstants.CLASS_INJECT; import static com.squareup.ideaplugin.dagger.DaggerConstants.MAX_USAGES; /** * Handles linking from constructor @Inject(ion) to @Provides. If the Constructor takes multiple * parameters, a dialog will pop-up asking the user which parameter type they'd like to look at. * * Aside from that popup, this is exactly like {@link FieldInjectToProvidesHandler}. */ public class ConstructorInjectToProvidesHandler implements GutterIconNavigationHandler<PsiElement> { @Override public void navigate(final MouseEvent mouseEvent, PsiElement psiElement) { if (!(psiElement instanceof PsiMethod)) { throw new IllegalStateException("Called with non-method: " + psiElement); } PsiMethod psiMethod = (PsiMethod) psiElement; PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); if (parameters.length == 1) { showUsages(mouseEvent, parameters[0]); } else { new PickTypeAction().startPickTypes(new RelativePoint(mouseEvent), parameters, new PickTypeAction.Callback() { @Override public void onParameterChosen(PsiParameter selected) { showUsages(mouseEvent, selected); } }); } } private void showUsages(final MouseEvent mouseEvent, final PsiParameter psiParameter) { // Check to see if class type of psiParameter has constructor with @Inject. Otherwise, proceed. if (navigateToConstructorIfProvider(psiParameter)) { return; } // If psiParameter is Set<T>, check if @Provides(type=SET) T providesT exists. // Also check map (TODO(radford): Add check for map). List<PsiType> paramTypes = PsiConsultantImpl.getTypeParameters(psiParameter); if (paramTypes.isEmpty()) { new ShowUsagesAction( new Decider.ConstructorParameterInjectDecider(psiParameter)).startFindUsages( PsiConsultantImpl.checkForLazyOrProvider(psiParameter), new RelativePoint(mouseEvent), PsiUtilBase.findEditor(psiParameter), MAX_USAGES); } else { ShowUsagesAction actions = new ShowUsagesAction( new Decider.CollectionElementParameterInjectDecider(psiParameter)); actions.setListener(new ShowUsagesAction.Listener() { @Override public void onFinished(boolean hasResults) { if (!hasResults) { new ShowUsagesAction( new Decider.ConstructorParameterInjectDecider(psiParameter)).startFindUsages( PsiConsultantImpl.checkForLazyOrProvider(psiParameter), new RelativePoint(mouseEvent), PsiUtilBase.findEditor(psiParameter), MAX_USAGES); } } }); actions.startFindUsages(((PsiClassType) paramTypes.get(0)).resolve(), new RelativePoint(mouseEvent), PsiUtilBase.findEditor(psiParameter), MAX_USAGES); } } private boolean navigateToConstructorIfProvider(PsiParameter psiParameter) { PsiTypeElement declaringTypeElement = psiParameter.getTypeElement(); PsiClass classElement = JavaPsiFacade.getInstance(psiParameter.getProject()).findClass( declaringTypeElement.getType().getCanonicalText(), declaringTypeElement.getResolveScope()); if (classElement == null) { return false; } for (PsiMethod method : classElement.getConstructors()) { if (PsiConsultantImpl.hasAnnotation(method, CLASS_INJECT) && navigateToElement(method)) { return true; } } return false; } private boolean navigateToElement(PsiElement element) { PsiElement navigationElement = element.getNavigationElement(); if (navigationElement != null && navigationElement instanceof Navigatable && ((Navigatable) navigationElement).canNavigate()) { ((Navigatable) navigationElement).navigate(true); return true; } return false; } }