Problem 17716. another Throwable: PsiImplUtil.getParameterIndex
[fedora-idea.git] / plugins / IntelliLang / src / org / intellij / plugins / intelliLang / inject / java / ConcatenationInjector.java
blob3282d5c8c22b1605e0a7859c71f6bf228d36a2b2
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.intellij.plugins.intelliLang.inject.java;
18 import com.intellij.lang.Language;
19 import com.intellij.lang.injection.ConcatenationAwareInjector;
20 import com.intellij.lang.injection.MultiHostRegistrar;
21 import com.intellij.openapi.util.Condition;
22 import com.intellij.openapi.util.Ref;
23 import com.intellij.openapi.util.TextRange;
24 import com.intellij.openapi.util.Trinity;
25 import com.intellij.openapi.util.text.StringUtil;
26 import com.intellij.psi.*;
27 import com.intellij.psi.search.LocalSearchScope;
28 import com.intellij.psi.search.searches.ReferencesSearch;
29 import com.intellij.psi.tree.IElementType;
30 import com.intellij.psi.util.PsiTreeUtil;
31 import com.intellij.psi.util.PsiUtil;
32 import com.intellij.util.PairProcessor;
33 import com.intellij.util.Processor;
34 import com.intellij.util.containers.ContainerUtil;
35 import gnu.trove.THashSet;
36 import org.intellij.plugins.intelliLang.Configuration;
37 import org.intellij.plugins.intelliLang.inject.config.BaseInjection;
38 import org.intellij.plugins.intelliLang.inject.InjectedLanguage;
39 import org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport;
40 import org.intellij.plugins.intelliLang.inject.InjectorUtils;
41 import org.intellij.plugins.intelliLang.util.AnnotationUtilEx;
42 import org.intellij.plugins.intelliLang.util.ContextComputationProcessor;
43 import org.jetbrains.annotations.NotNull;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.LinkedList;
48 import java.util.List;
50 /**
51 * @author cdr
53 public class ConcatenationInjector implements ConcatenationAwareInjector {
54 private final Configuration myInjectionConfiguration;
56 public ConcatenationInjector(Configuration configuration) {
57 myInjectionConfiguration = configuration;
60 public void getLanguagesToInject(@NotNull final MultiHostRegistrar registrar, @NotNull PsiElement... operands) {
61 final PsiFile containingFile = operands[0].getContainingFile();
62 processLiteralExpressionInjections(new PairProcessor<Language, List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>>>() {
63 public boolean process(final Language language, List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> list) {
64 InjectorUtils.registerInjection(language, list, containingFile, registrar);
65 return true;
67 }, operands);
70 private boolean processAnnotationInjections(final boolean unparsable, final PsiModifierListOwner annoElement, final PairProcessor<Language, List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>>> processor,
71 final PsiElement... operands) {
72 final PsiAnnotation[] annotations =
73 AnnotationUtilEx.getAnnotationFrom(annoElement, myInjectionConfiguration.getLanguageAnnotationPair(), true);
74 if (annotations.length > 0) {
75 final String id = AnnotationUtilEx.calcAnnotationValue(annotations, "value");
76 final String prefix = AnnotationUtilEx.calcAnnotationValue(annotations, "prefix");
77 final String suffix = AnnotationUtilEx.calcAnnotationValue(annotations, "suffix");
78 final BaseInjection injection = new BaseInjection(LanguageInjectionSupport.JAVA_SUPPORT_ID);
79 if (prefix != null) injection.setPrefix(prefix);
80 if (suffix != null) injection.setSuffix(suffix);
81 if (id != null) injection.setInjectedLanguageId(id);
82 processInjectionWithContext(unparsable, injection, processor, operands);
83 return true;
85 return false;
88 private void processLiteralExpressionInjections(final PairProcessor<Language, List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>>> processor,
89 final PsiElement... operands) {
90 processLiteralExpressionInjectionsInner(myInjectionConfiguration, new Processor<Info>() {
91 public boolean process(final Info info) {
92 if (processAnnotationInjections(info.unparsable, info.owner, processor, operands)) return false; // annotated element
93 for (BaseInjection injection : info.injections) {
94 processInjectionWithContext(info.unparsable, injection, processor, operands);
95 if (injection.isTerminal()) {
96 return false;
99 return true;
101 }, operands);
104 public static class Info {
105 final PsiModifierListOwner owner;
106 final PsiMethod method;
107 final List<BaseInjection> injections;
108 final boolean unparsable;
109 final int parameterIndex;
111 public Info(final PsiModifierListOwner owner,
112 final PsiMethod method,
113 final List<BaseInjection> injections,
114 final boolean unparsable,
115 final int parameterIndex) {
116 this.owner = owner;
117 this.method = method;
118 this.injections = injections;
119 this.unparsable = unparsable;
120 this.parameterIndex = parameterIndex;
124 public static void processLiteralExpressionInjectionsInner(final Configuration configuration, final Processor<Info> processor,
125 final PsiElement... operands) {
126 if (operands.length == 0) return;
127 final PsiElement firstOperand = operands[0];
128 final PsiElement topBlock = PsiUtil.getTopLevelEnclosingCodeBlock(firstOperand, null);
129 final LocalSearchScope searchScope = new LocalSearchScope(new PsiElement[]{topBlock instanceof PsiCodeBlock
130 ? topBlock : firstOperand.getContainingFile()}, "", true);
131 final THashSet<PsiModifierListOwner> visitedVars = new THashSet<PsiModifierListOwner>();
132 final LinkedList<PsiElement> places = new LinkedList<PsiElement>();
133 places.add(firstOperand);
134 boolean unparsable = false;
135 while (!places.isEmpty()) {
136 final PsiElement curPlace = places.removeFirst();
137 final PsiModifierListOwner owner = AnnotationUtilEx.getAnnotatedElementFor(curPlace, AnnotationUtilEx.LookupType.PREFER_CONTEXT);
138 if (owner == null) continue;
140 final PsiMethod psiMethod;
141 final int parameterIndex;
142 if (owner instanceof PsiParameter) {
143 final PsiElement declarationScope = ((PsiParameter)owner).getDeclarationScope();
144 psiMethod = declarationScope instanceof PsiMethod? (PsiMethod)declarationScope : null;
145 final PsiParameterList parameterList = psiMethod == null? null : ((PsiMethod)declarationScope).getParameterList();
146 // don't check catchblock parameters & etc.
147 if (parameterList == null || parameterList != owner.getParent()) continue;
148 parameterIndex = parameterList.getParameterIndex((PsiParameter)owner);
150 else if (owner instanceof PsiMethod) {
151 psiMethod = (PsiMethod)owner;
152 parameterIndex = -1;
154 else if (configuration.isResolveReferences() &&
155 owner instanceof PsiVariable && visitedVars.add(owner)) {
156 final PsiVariable variable = (PsiVariable)owner;
157 for (PsiReference psiReference : ReferencesSearch.search(variable, searchScope).findAll()) {
158 final PsiElement element = psiReference.getElement();
159 if (element instanceof PsiExpression) {
160 final PsiExpression refExpression = (PsiExpression)element;
161 places.add(refExpression);
162 if (!unparsable) {
163 unparsable = checkUnparsableReference(refExpression);
167 parameterIndex = -1;
168 psiMethod = null;
170 else {
171 parameterIndex = -1;
172 psiMethod = null;
174 final List<BaseInjection> injections;
175 if (psiMethod == null) {
176 injections = Collections.emptyList();
178 else {
179 injections = ContainerUtil.findAll(configuration.getInjections(LanguageInjectionSupport.JAVA_SUPPORT_ID), new Condition<BaseInjection>() {
180 public boolean value(final BaseInjection injection) {
181 return injection.acceptsPsiElement(owner);
185 final Info info = new Info(owner, psiMethod, injections, unparsable, parameterIndex);
186 if (!processor.process(info)) return;
190 private static void processInjectionWithContext(final boolean unparsable, final BaseInjection injection,
191 final PairProcessor<Language, List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>>> processor,
192 final PsiElement... operands) {
193 final Language language = InjectedLanguage.findLanguageById(injection.getInjectedLanguageId());
194 if (language == null) return;
195 final boolean separateFiles = !injection.isSingleFile() && StringUtil.isNotEmpty(injection.getValuePattern());
197 final Ref<Boolean> unparsableRef = Ref.create(unparsable);
198 final List<Object> objects = ContextComputationProcessor.collectOperands(injection.getPrefix(), injection.getSuffix(), unparsableRef, operands);
199 if (objects.isEmpty()) return;
200 final List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> result = new ArrayList<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>>();
201 final int len = objects.size();
202 for (int i = 0; i < len; i++) {
203 String curPrefix = null;
204 Object o = objects.get(i);
205 if (o instanceof String) {
206 curPrefix = (String)o;
207 if (i == len - 1) return; // IDEADEV-26751
208 o = objects.get(++i);
210 String curSuffix = null;
211 PsiLanguageInjectionHost curHost = null;
212 if (o instanceof PsiLanguageInjectionHost) {
213 curHost = (PsiLanguageInjectionHost)o;
214 if (i == len-2) {
215 final Object next = objects.get(i + 1);
216 if (next instanceof String) {
217 i ++;
218 curSuffix = (String)next;
222 if (curHost == null) {
223 unparsableRef.set(Boolean.TRUE);
225 else {
226 if (!(curHost instanceof PsiLiteralExpression)) {
227 result.add(Trinity.create(curHost, InjectedLanguage.create(injection.getInjectedLanguageId(), curPrefix, curSuffix, true),
228 ElementManipulators.getManipulator(curHost).getRangeInElement(curHost)));
230 else {
231 final List<TextRange> injectedArea = injection.getInjectedArea(curHost);
232 for (int j = 0, injectedAreaSize = injectedArea.size(); j < injectedAreaSize; j++) {
233 final TextRange textRange = injectedArea.get(j);
234 result.add(Trinity.create(
235 curHost, InjectedLanguage.create(injection.getInjectedLanguageId(),
236 (separateFiles || j == 0? curPrefix: ""),
237 (separateFiles || j == injectedAreaSize -1? curSuffix : ""),
238 true), textRange));
243 if (!result.isEmpty()) {
244 if (separateFiles) {
245 for (Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange> trinity : result) {
246 processor.process(language, Collections.singletonList(trinity));
249 else {
250 for (Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange> trinity : result) {
251 trinity.first.putUserData(LanguageInjectionSupport.HAS_UNPARSABLE_FRAGMENTS, unparsableRef.get());
253 processor.process(language, result);
258 private static boolean checkUnparsableReference(final PsiExpression refExpression) {
259 final PsiElement parent = refExpression.getParent();
260 if (parent instanceof PsiAssignmentExpression) {
261 final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
262 final IElementType operation = assignmentExpression.getOperationTokenType();
263 if (assignmentExpression.getLExpression() == refExpression && JavaTokenType.PLUSEQ.equals(operation)) {
264 return true;
267 else if (parent instanceof PsiBinaryExpression) {
268 return true;
270 return false;