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
;
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
);
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
);
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()) {
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
) {
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
;
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
);
163 unparsable
= checkUnparsableReference(refExpression
);
174 final List
<BaseInjection
> injections
;
175 if (psiMethod
== null) {
176 injections
= Collections
.emptyList();
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
;
215 final Object next
= objects
.get(i
+ 1);
216 if (next
instanceof String
) {
218 curSuffix
= (String
)next
;
222 if (curHost
== null) {
223 unparsableRef
.set(Boolean
.TRUE
);
226 if (!(curHost
instanceof PsiLiteralExpression
)) {
227 result
.add(Trinity
.create(curHost
, InjectedLanguage
.create(injection
.getInjectedLanguageId(), curPrefix
, curSuffix
, true),
228 ElementManipulators
.getManipulator(curHost
).getRangeInElement(curHost
)));
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
: ""),
243 if (!result
.isEmpty()) {
245 for (Trinity
<PsiLanguageInjectionHost
, InjectedLanguage
, TextRange
> trinity
: result
) {
246 processor
.process(language
, Collections
.singletonList(trinity
));
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
)) {
267 else if (parent
instanceof PsiBinaryExpression
) {