| /* |
| * 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 com.intellij.codeInsight.daemon.impl.quickfix; |
| |
| import com.intellij.codeInsight.daemon.QuickFixBundle; |
| import com.intellij.codeInsight.intention.IntentionAction; |
| import com.intellij.codeInsight.template.TemplateBuilderImpl; |
| import com.intellij.codeInsight.template.TemplateManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.HashSet; |
| import gnu.trove.TObjectIntHashMap; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.util.Collection; |
| import java.util.Comparator; |
| import java.util.SortedSet; |
| import java.util.TreeSet; |
| |
| /** |
| * @author Dmitry Batkovich |
| */ |
| public class AddMissingRequiredAnnotationParametersFix implements IntentionAction { |
| private static final Logger LOG = Logger.getInstance(AddMissingRequiredAnnotationParametersFix.class); |
| |
| private final PsiAnnotation myAnnotation; |
| private final PsiMethod[] myAnnotationMethods; |
| private final Collection<String> myMissedElements; |
| |
| public AddMissingRequiredAnnotationParametersFix(final PsiAnnotation annotation, |
| final PsiMethod[] annotationMethods, |
| final Collection<String> missedElements) { |
| if (missedElements.isEmpty()) { |
| throw new IllegalArgumentException("missedElements can't be empty"); |
| } |
| myAnnotation = annotation; |
| myAnnotationMethods = annotationMethods; |
| myMissedElements = missedElements; |
| } |
| |
| @NotNull |
| @Override |
| public String getText() { |
| return myMissedElements.size() == 1 |
| ? QuickFixBundle.message("add.missing.annotation.single.parameter.fix", ContainerUtil.getFirstItem(myMissedElements)) |
| : QuickFixBundle.message("add.missing.annotation.parameters.fix", StringUtil.join(myMissedElements, ", ")); |
| } |
| |
| @NotNull |
| @Override |
| public String getFamilyName() { |
| return QuickFixBundle.message("annotations.fix"); |
| } |
| |
| @Override |
| public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { |
| return true; |
| } |
| |
| @Override |
| public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { |
| final PsiNameValuePair[] addedParameters = myAnnotation.getParameterList().getAttributes(); |
| |
| final TObjectIntHashMap<String> annotationsOrderMap = getAnnotationsOrderMap(); |
| final SortedSet<Pair<String, PsiAnnotationMemberValue>> |
| newParameters = new TreeSet<Pair<String, PsiAnnotationMemberValue>>(new Comparator<Pair<String, PsiAnnotationMemberValue>>() { |
| @Override |
| public int compare(final Pair<String, PsiAnnotationMemberValue> o1, final Pair<String, PsiAnnotationMemberValue> o2) { |
| return annotationsOrderMap.get(o1.getFirst()) - annotationsOrderMap.get(o2.getFirst()); |
| } |
| }); |
| final boolean order = isAlreadyAddedOrdered(annotationsOrderMap, addedParameters); |
| if (order) { |
| if (addedParameters.length != 0) { |
| final PsiAnnotationParameterList parameterList = myAnnotation.getParameterList(); |
| parameterList.deleteChildRange(addedParameters[0], addedParameters[addedParameters.length - 1]); |
| for (final PsiNameValuePair addedParameter : addedParameters) { |
| final String name = addedParameter.getName(); |
| final PsiAnnotationMemberValue value = addedParameter.getValue(); |
| if (name == null || value == null) { |
| LOG.error(String.format("Invalid annotation parameter name = %s, value = %s", name, value)); |
| continue; |
| } |
| newParameters.add(Pair.create(name, value)); |
| } |
| } |
| } |
| |
| final PsiExpression nullValue = JavaPsiFacade.getElementFactory(myAnnotation.getProject()).createExpressionFromText(PsiKeyword.NULL, null); |
| for (final String misssedParameter : myMissedElements) { |
| newParameters.add(Pair.<String, PsiAnnotationMemberValue>create(misssedParameter, nullValue)); |
| } |
| |
| TemplateBuilderImpl builder = null; |
| for (final Pair<String, PsiAnnotationMemberValue> newParameter : newParameters) { |
| final PsiAnnotationMemberValue value = |
| myAnnotation.setDeclaredAttributeValue(newParameter.getFirst(), newParameter.getSecond()); |
| if (myMissedElements.contains(newParameter.getFirst())) { |
| if (builder == null) { |
| builder = new TemplateBuilderImpl(myAnnotation.getParameterList()); |
| } |
| builder.replaceElement(value, new EmptyExpression(), true); |
| } |
| } |
| |
| editor.getCaretModel().moveToOffset(myAnnotation.getParameterList().getTextRange().getStartOffset()); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); |
| final Document document = documentManager.getDocument(file); |
| if (document == null) { |
| throw new IllegalStateException(); |
| } |
| documentManager.doPostponedOperationsAndUnblockDocument(document); |
| TemplateManager.getInstance(project).startTemplate(editor, builder.buildInlineTemplate(), null); |
| } |
| |
| @Override |
| public boolean startInWriteAction() { |
| return true; |
| } |
| |
| private TObjectIntHashMap<String> getAnnotationsOrderMap() { |
| final TObjectIntHashMap<String> map = new TObjectIntHashMap<String>(); |
| for (int i = 0; i < myAnnotationMethods.length; i++) { |
| map.put(myAnnotationMethods[i].getName(), i); |
| } |
| return map; |
| } |
| |
| private static boolean isAlreadyAddedOrdered(final TObjectIntHashMap<String> orderMap, final PsiNameValuePair[] addedParameters) { |
| if (addedParameters.length <= 1) { |
| return true; |
| } |
| int previousOrder = orderMap.get(addedParameters[0].getName()); |
| for (int i = 1; i < addedParameters.length; i++) { |
| final int currentOrder = orderMap.get(addedParameters[i].getName()); |
| if (currentOrder < previousOrder) { |
| return false; |
| } |
| previousOrder = currentOrder; |
| } |
| return true; |
| } |
| } |