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 com
.intellij
.psi
.impl
.source
.tree
.injected
;
18 import com
.intellij
.lang
.injection
.ConcatenationAwareInjector
;
19 import com
.intellij
.lang
.injection
.InjectedLanguageManager
;
20 import com
.intellij
.lang
.injection
.MultiHostInjector
;
21 import com
.intellij
.lang
.injection
.MultiHostRegistrar
;
22 import com
.intellij
.openapi
.components
.ProjectComponent
;
23 import com
.intellij
.openapi
.extensions
.*;
24 import com
.intellij
.openapi
.project
.Project
;
25 import com
.intellij
.openapi
.util
.Key
;
26 import com
.intellij
.openapi
.util
.ModificationTracker
;
27 import com
.intellij
.psi
.*;
28 import com
.intellij
.psi
.impl
.PsiManagerEx
;
29 import com
.intellij
.psi
.impl
.PsiParameterizedCachedValue
;
30 import com
.intellij
.psi
.util
.*;
31 import com
.intellij
.util
.containers
.ContainerUtil
;
32 import org
.jetbrains
.annotations
.NonNls
;
33 import org
.jetbrains
.annotations
.NotNull
;
34 import org
.jetbrains
.annotations
.Nullable
;
36 import java
.util
.ArrayList
;
37 import java
.util
.Arrays
;
38 import java
.util
.List
;
39 import java
.util
.concurrent
.atomic
.AtomicReference
;
44 public class JavaConcatenationInjectorManager
implements ProjectComponent
, ModificationTracker
{
45 public static final ExtensionPointName
<ConcatenationAwareInjector
> CONCATENATION_INJECTOR_EP_NAME
= ExtensionPointName
.create("com.intellij.concatenationAwareInjector");
46 private final AtomicReference
<MultiHostInjector
> myRegisteredConcatenationAdapter
= new AtomicReference
<MultiHostInjector
>();
47 private final InjectedLanguageManager myInjectedLanguageManager
;
48 private volatile long myModificationCounter
;
49 private static final ConcatenationPsiCachedValueProvider CONCATENATION_PSI_CACHED_VALUE_PROVIDER
= new ConcatenationPsiCachedValueProvider();
51 public JavaConcatenationInjectorManager(Project project
, InjectedLanguageManager injectedLanguageManager
, PsiManagerEx psiManagerEx
) {
52 myInjectedLanguageManager
= injectedLanguageManager
;
53 final ExtensionPoint
<ConcatenationAwareInjector
> concatPoint
= Extensions
.getArea(project
).getExtensionPoint(CONCATENATION_INJECTOR_EP_NAME
);
54 concatPoint
.addExtensionPointListener(new ExtensionPointListener
<ConcatenationAwareInjector
>() {
55 public void extensionAdded(ConcatenationAwareInjector injector
, @Nullable PluginDescriptor pluginDescriptor
) {
56 registerConcatenationInjector(injector
);
59 public void extensionRemoved(ConcatenationAwareInjector injector
, @Nullable PluginDescriptor pluginDescriptor
) {
60 unregisterConcatenationInjector(injector
);
63 psiManagerEx
.registerRunnableToRunOnAnyChange(new Runnable() {
65 myModificationCounter
++; // clear caches even on non-physical changes
70 public void projectOpened() {
74 public void projectClosed() {
80 public String
getComponentName() {
81 return "JavaConcatenationInjectorManager";
84 public void initComponent() {
88 public void disposeComponent() {
92 public static JavaConcatenationInjectorManager
getInstance(final Project project
) {
93 return project
.getComponent(JavaConcatenationInjectorManager
.class);
96 public long getModificationCount() {
97 return myModificationCounter
;
100 private static class ConcatenationPsiCachedValueProvider
implements ParameterizedCachedValueProvider
<Places
, PsiElement
> {
101 public CachedValueProvider
.Result
<Places
> compute(PsiElement context
) {
102 PsiElement element
= context
;
103 PsiElement parent
= context
.getParent();
104 while (parent
instanceof PsiBinaryExpression
&& ((PsiBinaryExpression
)parent
).getOperationSign().getTokenType() == JavaTokenType
.PLUS
105 || parent
instanceof PsiAssignmentExpression
&& ((PsiAssignmentExpression
)parent
).getOperationSign().getTokenType() == JavaTokenType
.PLUSEQ
106 || parent
instanceof PsiConditionalExpression
&& ((PsiConditionalExpression
)parent
).getCondition() != element
107 || parent
instanceof PsiTypeCastExpression
108 || parent
instanceof PsiParenthesizedExpression
) {
110 parent
= parent
.getParent();
113 PsiElement
[] operands
;
115 if (element
instanceof PsiBinaryExpression
|| element
instanceof PsiAssignmentExpression
) {
116 List
<PsiElement
> operandList
= new ArrayList
<PsiElement
>();
117 collectOperands(element
, operandList
);
118 operands
= operandList
.toArray(new PsiElement
[operandList
.size()]);
122 operands
= new PsiElement
[]{context
};
125 Project project
= context
.getProject();
127 MultiHostRegistrarImpl registrar
= new MultiHostRegistrarImpl(project
, context
.getContainingFile(), anchor
);
128 JavaConcatenationInjectorManager concatenationInjectorManager
= getInstance(project
);
129 for (ConcatenationAwareInjector concatenationInjector
: concatenationInjectorManager
.myConcatenationInjectors
) {
130 concatenationInjector
.getLanguagesToInject(registrar
, operands
);
133 CachedValueProvider
.Result
<Places
> result
= new CachedValueProvider
.Result
<Places
>(registrar
.result
, PsiModificationTracker
.MODIFICATION_COUNT
, concatenationInjectorManager
);
135 if (registrar
.result
!= null) {
136 // store this everywhere
137 ParameterizedCachedValue
<Places
, PsiElement
> cachedValue
=
138 CachedValuesManager
.getManager(context
.getProject()).createParameterizedCachedValue(this, false);
139 ((PsiParameterizedCachedValue
<Places
, PsiElement
>)cachedValue
).setValue(result
);
141 for (PsiElement operand
: operands
) {
142 operand
.putUserData(INJECTED_PSI_IN_CONCATENATION
, cachedValue
);
144 anchor
.putUserData(INJECTED_PSI_IN_CONCATENATION
, cachedValue
);
145 context
.putUserData(INJECTED_PSI_IN_CONCATENATION
, cachedValue
);
152 private static final Key
<ParameterizedCachedValue
<Places
, PsiElement
>> INJECTED_PSI_IN_CONCATENATION
= Key
.create("INJECTED_PSI_IN_CONCATENATION");
153 private class Concatenation2InjectorAdapter
implements MultiHostInjector
{
154 public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar
, @NotNull PsiElement context
) {
155 if (myConcatenationInjectors
.isEmpty()) return;
157 ParameterizedCachedValue
<Places
, PsiElement
> cachedValue
= context
.getUserData(INJECTED_PSI_IN_CONCATENATION
);
159 if (cachedValue
== null) {
160 CachedValueProvider
.Result
<Places
> res
= CONCATENATION_PSI_CACHED_VALUE_PROVIDER
.compute(context
);
161 result
= res
== null ?
null : res
.getValue();
164 result
= cachedValue
.getValue(context
);
166 if (result
!= null) {
167 ((MultiHostRegistrarImpl
)registrar
).addToResults(result
);
172 public List
<?
extends Class
<?
extends PsiElement
>> elementsToInjectIn() {
173 return Arrays
.asList(PsiLiteralExpression
.class);
177 private static void collectOperands(PsiElement expression
, List
<PsiElement
> operands
) {
178 if (expression
instanceof PsiBinaryExpression
) {
179 final PsiBinaryExpression binaryExpression
= (PsiBinaryExpression
)expression
;
180 collectOperands(binaryExpression
.getLOperand(), operands
);
181 collectOperands(binaryExpression
.getROperand(), operands
);
183 else if (expression
!= null) {
184 operands
.add(expression
);
188 private final List
<ConcatenationAwareInjector
> myConcatenationInjectors
= ContainerUtil
.createEmptyCOWList();
189 public void registerConcatenationInjector(@NotNull ConcatenationAwareInjector injector
) {
190 myConcatenationInjectors
.add(injector
);
191 concatenationInjectorsChanged();
194 public boolean unregisterConcatenationInjector(@NotNull ConcatenationAwareInjector injector
) {
195 boolean removed
= myConcatenationInjectors
.remove(injector
);
196 concatenationInjectorsChanged();
200 private void concatenationInjectorsChanged() {
201 myModificationCounter
++;
202 if (myConcatenationInjectors
.isEmpty()) {
203 MultiHostInjector prev
= myRegisteredConcatenationAdapter
.getAndSet(null);
205 myInjectedLanguageManager
.unregisterMultiHostInjector(prev
);
209 MultiHostInjector adapter
= new Concatenation2InjectorAdapter();
210 if (myRegisteredConcatenationAdapter
.compareAndSet(null, adapter
)) {
211 myInjectedLanguageManager
.registerMultiHostInjector(adapter
);