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
;
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*/;
80 public String
getId() {
81 return XML_SUPPORT_ID
;
85 public Class
[] getPatternClasses() {
86 return new Class
[] {XmlPatterns
.class};
89 public boolean useDefaultInjector(final PsiElement host
) {
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());
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());
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());
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();
153 builder
.addOkAction();
154 builder
.addCancelAction();
155 builder
.setCenterPanel(panel
.getComponent());
156 builder
.setTitle(EditInjectionSettingsAction
.EDIT_INJECTION_TITLE
);
157 builder
.setOkOperation(new Runnable() {
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
);
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;
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;
184 result
= new XmlAttributeInjection().copyFrom(injection
);
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));
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
);
207 if (result
instanceof XmlAttributeInjection
) {
208 ((XmlAttributeInjection
)result
).setAttributeName(pair1
.first
);
209 ((XmlAttributeInjection
)result
).setAttributeNamespace(StringUtil
.notNullize(pair1
.second
));
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
));
221 //result.initializePlaces(false);
222 //if (!place.getText().equals(result.getInjectionPlaces().get(0).getText())) {
228 if (result
!= null) result
.getInjectionPlaces().clear();
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();
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
);
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();
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
);
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
);
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
) {
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
) {
325 public void actionPerformed(final AnActionEvent e
) {
326 final AbstractTagInjection injection
= showInjectionUI(project
, new XmlAttributeInjection());
327 if (injection
!= null) consumer
.consume(injection
);
334 public AnAction
createEditAction(final Project project
, final Factory
<BaseInjection
> producer
) {
335 return new AnAction() {
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);