18722 assert: CaretModelImpl.validateCallContext
[fedora-idea.git] / plugins / IntelliLang / src / org / intellij / plugins / intelliLang / inject / xml / XmlLanguageInjectionSupport.java
blob21b1427277f1d6a7b40ae9cf4b0d33b0913c4d83
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.
17 package org.intellij.plugins.intelliLang.inject.xml;
19 import com.intellij.lang.Language;
20 import com.intellij.openapi.actionSystem.AnAction;
21 import com.intellij.openapi.actionSystem.AnActionEvent;
22 import com.intellij.openapi.options.Configurable;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.ui.DialogBuilder;
25 import com.intellij.openapi.ui.DialogWrapper;
26 import com.intellij.openapi.util.Factory;
27 import com.intellij.openapi.util.Pair;
28 import com.intellij.openapi.util.text.StringUtil;
29 import com.intellij.patterns.XmlPatterns;
30 import com.intellij.psi.PsiElement;
31 import com.intellij.psi.PsiLanguageInjectionHost;
32 import com.intellij.psi.util.PsiTreeUtil;
33 import com.intellij.psi.xml.XmlAttribute;
34 import com.intellij.psi.xml.XmlAttributeValue;
35 import com.intellij.psi.xml.XmlTag;
36 import com.intellij.psi.xml.XmlText;
37 import com.intellij.util.Consumer;
38 import com.intellij.util.Icons;
39 import com.intellij.util.containers.ContainerUtil;
40 import org.intellij.plugins.intelliLang.Configuration;
41 import org.intellij.plugins.intelliLang.inject.AbstractLanguageInjectionSupport;
42 import org.intellij.plugins.intelliLang.inject.EditInjectionSettingsAction;
43 import org.intellij.plugins.intelliLang.inject.InjectLanguageAction;
44 import org.intellij.plugins.intelliLang.inject.config.*;
45 import org.intellij.plugins.intelliLang.inject.config.ui.AbstractInjectionPanel;
46 import org.intellij.plugins.intelliLang.inject.config.ui.XmlAttributePanel;
47 import org.intellij.plugins.intelliLang.inject.config.ui.XmlTagPanel;
48 import org.intellij.plugins.intelliLang.inject.config.ui.configurables.XmlAttributeInjectionConfigurable;
49 import org.intellij.plugins.intelliLang.inject.config.ui.configurables.XmlTagInjectionConfigurable;
50 import org.jdom.Element;
51 import org.jetbrains.annotations.NotNull;
52 import org.jetbrains.annotations.Nullable;
54 import java.util.ArrayList;
55 import java.util.Collections;
56 import java.util.regex.Matcher;
57 import java.util.regex.Pattern;
59 /**
60 * @author Gregory.Shrago
62 public class XmlLanguageInjectionSupport extends AbstractLanguageInjectionSupport {
64 private static boolean isMine(final PsiLanguageInjectionHost host) {
65 if (host instanceof XmlAttributeValue) {
66 final PsiElement p = host.getParent();
67 if (p instanceof XmlAttribute) {
68 final String s = ((XmlAttribute)p).getName();
69 return !(s.equals("xmlns") || s.startsWith("xmlns:"));
72 else if (host instanceof XmlText) {
73 final XmlTag tag = ((XmlText)host).getParentTag();
74 return tag != null/* && tag.getValue().getTextElements().length == 1 && tag.getSubTags().length == 0*/;
76 return false;
79 @NotNull
80 public String getId() {
81 return XML_SUPPORT_ID;
84 @NotNull
85 public Class[] getPatternClasses() {
86 return new Class[] {XmlPatterns.class};
89 public boolean useDefaultInjector(final PsiElement host) {
90 return false;
93 public boolean addInjectionInPlace(final Language language, final PsiLanguageInjectionHost psiElement) {
94 if (!isMine(psiElement)) return false;
95 if (psiElement instanceof XmlAttributeValue) {
96 return doInjectInAttributeValue((XmlAttributeValue)psiElement, language.getID());
98 else if (psiElement instanceof XmlText) {
99 return doInjectInXmlText((XmlText)psiElement, language.getID());
101 return false;
104 public boolean removeInjectionInPlace(final PsiLanguageInjectionHost host) {
105 if (!isMine(host)) return false;
106 final Configuration configuration = Configuration.getInstance();
107 final ArrayList<BaseInjection> injections = collectInjections(host, configuration);
108 if (injections.isEmpty()) return false;
109 final ArrayList<BaseInjection> newInjections = new ArrayList<BaseInjection>();
110 for (BaseInjection injection : injections) {
111 final BaseInjection newInjection = injection.copy();
112 newInjection.setPlaceEnabled(null, false);
114 Configuration.getInstance().replaceInjectionsWithUndo(
115 host.getProject(), newInjections, injections, Collections.<PsiElement>emptyList());
116 return true;
119 public boolean editInjectionInPlace(final PsiLanguageInjectionHost host) {
120 if (!isMine(host)) return false;
121 final Configuration configuration = Configuration.getInstance();
122 final ArrayList<BaseInjection> injections = collectInjections(host, configuration);
123 if (injections.isEmpty()) return false;
124 final Project project = host.getProject();
125 final BaseInjection originalInjection = injections.get(0);
126 final BaseInjection xmlInjection = createFrom(originalInjection, host);
127 if (xmlInjection == null) return false;
128 final AbstractTagInjection newInjection = showInjectionUI(project, xmlInjection);
129 if (newInjection != null) {
130 newInjection.mergeOriginalPlacesFrom(originalInjection, true);
131 Configuration.getInstance().replaceInjectionsWithUndo(
132 project, Collections.singletonList(xmlInjection),
133 Collections.singletonList(originalInjection),
134 Collections.<PsiElement>emptyList());
136 return true;
139 @Nullable
140 private AbstractTagInjection showInjectionUI(final Project project, final BaseInjection xmlInjection) {
141 final DialogBuilder builder = new DialogBuilder(project);
142 final AbstractInjectionPanel panel;
143 if (xmlInjection instanceof XmlTagInjection) {
144 panel = new XmlTagPanel((XmlTagInjection)xmlInjection, project);
145 builder.setHelpId("reference.settings.injection.language.injection.settings.xml.tag");
147 else if (xmlInjection instanceof XmlAttributeInjection) {
148 panel = new XmlAttributePanel((XmlAttributeInjection)xmlInjection, project);
149 builder.setHelpId("reference.settings.injection.language.injection.settings.xml.attribute");
151 else throw new AssertionError();
152 panel.reset();
153 builder.addOkAction();
154 builder.addCancelAction();
155 builder.setCenterPanel(panel.getComponent());
156 builder.setTitle(EditInjectionSettingsAction.EDIT_INJECTION_TITLE);
157 builder.setOkOperation(new Runnable() {
158 public void run() {
159 panel.apply();
160 builder.getDialogWrapper().close(DialogWrapper.OK_EXIT_CODE);
163 if (builder.show() == DialogWrapper.OK_EXIT_CODE) {
164 xmlInjection.initializePlaces(false);
165 return new AbstractTagInjection().copyFrom(xmlInjection);
167 return null;
170 @Nullable
171 private static BaseInjection createFrom(final BaseInjection injection, final PsiLanguageInjectionHost host) {
172 final PsiElement element;
173 AbstractTagInjection result;
174 if (host instanceof XmlText) {
175 final XmlTag xmlTag = ((XmlText)host).getParentTag();
176 if (xmlTag == null) return null;
177 element = xmlTag;
178 result = new XmlTagInjection().copyFrom(injection);
180 else if (host instanceof XmlAttributeValue) {
181 final PsiElement parent = host.getParent();
182 if (!(parent instanceof XmlAttribute)) return null;
183 element = parent;
184 result = new XmlAttributeInjection().copyFrom(injection);
186 else {
187 result = null;
188 element = null;
190 final Pattern pattern = Pattern.compile("withLocalName[^\"]*\"([^\"]*)\"\\)+(?:\\.withNamespace[^\"]*\"([^\"]*)\")?");
191 for (InjectionPlace place : injection.getInjectionPlaces()) {
192 if (element == null || place.getElementPattern() != null && place.getElementPattern().accepts(element)) {
193 final Matcher matcher = pattern.matcher(place.getText());
194 if (matcher.find()) {
195 final Pair<String, String> pair1 = Pair.create(matcher.group(1), matcher.group(2));
196 final Pair<String, String> pair2;
197 if (matcher.find(Math.max(matcher.end(1), matcher.end(2)))) {
198 pair2 = Pair.create(matcher.group(1), matcher.group(2));
200 else pair2 = null;
202 if (result == null) {
203 if (place.getText().startsWith("xmlTag")) result = new XmlTagInjection().copyFrom(injection);
204 else if (place.getText().startsWith("xmlAttribute")) result = new XmlAttributeInjection().copyFrom(injection);
205 else continue;
207 if (result instanceof XmlAttributeInjection) {
208 ((XmlAttributeInjection)result).setAttributeName(pair1.first);
209 ((XmlAttributeInjection)result).setAttributeNamespace(StringUtil.notNullize(pair1.second));
210 if (pair2 != null) {
211 result.setTagName(pair2.first);
212 result.setTagNamespace(StringUtil.notNullize(pair2.second));
215 else if (result instanceof XmlTagInjection) {
216 result.setTagName(pair1.first);
217 result.setTagNamespace(StringUtil.notNullize(pair1.second));
219 else continue;
220 // for debugging
221 //result.initializePlaces(false);
222 //if (!place.getText().equals(result.getInjectionPlaces().get(0).getText())) {
224 break;
228 if (result != null) result.getInjectionPlaces().clear();
229 return result;
232 public BaseInjection createInjection(final Element element) {
233 if (element.getName().equals(XmlAttributeInjection.class.getSimpleName())) {
234 return new XmlAttributeInjection();
236 else if (element.getName().equals(XmlTagInjection.class.getSimpleName())) {
237 return new XmlTagInjection();
239 return new AbstractTagInjection();
242 public Configurable[] createSettings(final Project project, final Configuration configuration) {
243 return new Configurable[0];
246 private static boolean doInjectInXmlText(final XmlText host, final String languageId) {
247 final XmlTag tag = host.getParentTag();
248 if (tag != null) {
249 final XmlTagInjection injection = new XmlTagInjection();
250 injection.setInjectedLanguageId(languageId);
251 injection.setTagName(tag.getLocalName());
252 injection.setTagNamespace(tag.getNamespace());
253 doEditInjection(host.getProject(), injection);
254 return true;
256 return false;
259 private static void doEditInjection(final Project project, final XmlTagInjection template) {
260 final AbstractTagInjection originalInjection = (AbstractTagInjection)Configuration.getInstance().findExistingInjection(template);
262 final XmlTagInjection newInjection = originalInjection == null? template : new XmlTagInjection().copyFrom(originalInjection);
263 if (InjectLanguageAction.doEditConfigurable(project, new XmlTagInjectionConfigurable(newInjection, null, project))) {
264 Configuration.getInstance().replaceInjectionsWithUndo(
265 project, Collections.singletonList(newInjection),
266 ContainerUtil.createMaybeSingletonList(originalInjection),
267 Collections.<PsiElement>emptyList());
271 private static boolean doInjectInAttributeValue(final XmlAttributeValue host, final String languageId) {
272 final XmlAttribute attribute = PsiTreeUtil.getParentOfType(host, XmlAttribute.class, true);
273 final XmlTag tag = attribute == null? null : attribute.getParent();
274 if (tag != null) {
275 final XmlAttributeInjection injection = new XmlAttributeInjection();
276 injection.setInjectedLanguageId(languageId);
277 injection.setAttributeName(attribute.getLocalName());
278 injection.setAttributeNamespace(attribute.getNamespace());
279 injection.setTagName(tag.getLocalName());
280 injection.setTagNamespace(tag.getNamespace());
281 doEditInjection(host.getProject(), injection);
282 return true;
284 return false;
287 private static void doEditInjection(final Project project, final XmlAttributeInjection template) {
288 final Configuration configuration = Configuration.getInstance();
289 template.initializePlaces(false);
290 final BaseInjection originalInjection = configuration.findExistingInjection(template);
291 final BaseInjection newInjection = originalInjection == null ? template : originalInjection.copy();
292 if (InjectLanguageAction.doEditConfigurable(project, new XmlAttributeInjectionConfigurable((XmlAttributeInjection)newInjection, null, project))) {
293 Configuration.getInstance().replaceInjectionsWithUndo(
294 project, Collections.singletonList(newInjection),
295 ContainerUtil.createMaybeSingletonList(originalInjection),
296 Collections.<PsiElement>emptyList());
300 private static ArrayList<BaseInjection> collectInjections(final PsiLanguageInjectionHost host,
301 final Configuration configuration) {
302 final ArrayList<BaseInjection> result = new ArrayList<BaseInjection>();
303 final PsiElement element = host instanceof XmlText? ((XmlText)host).getParentTag() :
304 host instanceof XmlAttributeValue? host.getParent(): host;
305 for (BaseInjection injection : configuration.getInjections(XML_SUPPORT_ID)) {
306 if (injection.acceptsPsiElement(element)) {
307 result.add(injection);
310 return result;
313 @Override
314 public AnAction[] createAddActions(final Project project, final Consumer<BaseInjection> consumer) {
315 return new AnAction[] {
316 new AnAction("XML Tag Injection", null, Icons.XML_TAG_ICON) {
317 @Override
318 public void actionPerformed(final AnActionEvent e) {
319 final AbstractTagInjection newInjection = showInjectionUI(project, new XmlTagInjection());
320 if (newInjection != null) consumer.consume(newInjection);
323 new AnAction("XML Attribute Injection", null, Icons.ANNOTATION_TYPE_ICON) {
324 @Override
325 public void actionPerformed(final AnActionEvent e) {
326 final AbstractTagInjection injection = showInjectionUI(project, new XmlAttributeInjection());
327 if (injection != null) consumer.consume(injection);
333 @Override
334 public AnAction createEditAction(final Project project, final Factory<BaseInjection> producer) {
335 return new AnAction() {
336 @Override
337 public void actionPerformed(final AnActionEvent e) {
338 final BaseInjection originalInjection = producer.create();
339 final BaseInjection injection = createFrom(originalInjection, null);
340 if (injection != null) {
341 final AbstractTagInjection newInjection = showInjectionUI(project, injection);
342 if (newInjection != null) {
343 originalInjection.copyFrom(newInjection);
344 originalInjection.initializePlaces(true);