deprecation removed
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / impl / source / tree / injected / JavaConcatenationInjectorManager.java
blobf067d7eb25e0ee1c35556375fda5bee119712348
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 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;
41 /**
42 * @author cdr
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);
62 });
63 psiManagerEx.registerRunnableToRunOnAnyChange(new Runnable() {
64 public void run() {
65 myModificationCounter++; // clear caches even on non-physical changes
67 });
70 public void projectOpened() {
74 public void projectClosed() {
78 @NonNls
79 @NotNull
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) {
109 element = parent;
110 parent = parent.getParent();
113 PsiElement[] operands;
114 PsiElement anchor;
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()]);
119 anchor = element;
121 else {
122 operands = new PsiElement[]{context};
123 anchor = 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);
148 return result;
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);
158 Places result;
159 if (cachedValue == null) {
160 CachedValueProvider.Result<Places> res = CONCATENATION_PSI_CACHED_VALUE_PROVIDER.compute(context);
161 result = res == null ? null : res.getValue();
163 else {
164 result = cachedValue.getValue(context);
166 if (result != null) {
167 ((MultiHostRegistrarImpl)registrar).addToResults(result);
171 @NotNull
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();
197 return removed;
200 private void concatenationInjectorsChanged() {
201 myModificationCounter++;
202 if (myConcatenationInjectors.isEmpty()) {
203 MultiHostInjector prev = myRegisteredConcatenationAdapter.getAndSet(null);
204 if (prev != null) {
205 myInjectedLanguageManager.unregisterMultiHostInjector(prev);
208 else {
209 MultiHostInjector adapter = new Concatenation2InjectorAdapter();
210 if (myRegisteredConcatenationAdapter.compareAndSet(null, adapter)) {
211 myInjectedLanguageManager.registerMultiHostInjector(adapter);