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
.xml
;
18 import com
.intellij
.openapi
.editor
.Editor
;
19 import com
.intellij
.openapi
.module
.ModuleUtil
;
20 import com
.intellij
.openapi
.project
.Project
;
21 import com
.intellij
.openapi
.util
.Pair
;
22 import com
.intellij
.openapi
.util
.text
.StringUtil
;
23 import com
.intellij
.openapi
.vfs
.VirtualFile
;
24 import com
.intellij
.psi
.PsiElement
;
25 import com
.intellij
.psi
.PsiFile
;
26 import com
.intellij
.psi
.XmlElementFactory
;
27 import com
.intellij
.psi
.codeStyle
.CodeStyleManager
;
28 import com
.intellij
.psi
.impl
.source
.xml
.TagNameReference
;
29 import com
.intellij
.psi
.xml
.*;
30 import com
.intellij
.util
.IncorrectOperationException
;
31 import com
.intellij
.xml
.index
.XmlNamespaceIndex
;
32 import com
.intellij
.xml
.index
.XmlTagNamesIndex
;
33 import com
.intellij
.xml
.util
.XmlUtil
;
34 import org
.jetbrains
.annotations
.NonNls
;
35 import org
.jetbrains
.annotations
.NotNull
;
36 import org
.jetbrains
.annotations
.Nullable
;
41 * @author Dmitry Avdeev
43 public class DefaultXmlExtension
extends XmlExtension
{
45 public boolean isAvailable(final PsiFile file
) {
50 public List
<Pair
<String
,String
>> getAvailableTagNames(@NotNull final XmlFile file
, @NotNull final XmlTag context
) {
52 final Set
<String
> namespaces
= new HashSet
<String
>(Arrays
.asList(context
.knownNamespaces()));
53 final XmlSchemaProvider provider
= XmlSchemaProvider
.getAvailableProvider(file
);
54 if (provider
!= null) {
55 namespaces
.addAll(provider
.getAvailableNamespaces(file
, null));
57 final ArrayList
<String
> nsInfo
= new ArrayList
<String
>();
58 final String
[] names
= TagNameReference
.getTagNameVariants(context
, namespaces
, nsInfo
);
59 final List
<Pair
<String
, String
>> set
= new ArrayList
<Pair
<String
,String
>>(names
.length
);
60 final Iterator
<String
> iterator
= nsInfo
.iterator();
61 for (String name
: names
) {
62 final int pos
= name
.indexOf(':');
63 final String s
= pos
>= 0 ? name
.substring(pos
+ 1) : name
;
64 set
.add(Pair
.create(s
, iterator
.next()));
70 public Set
<String
> getNamespacesByTagName(@NotNull final String tagName
, @NotNull final XmlFile context
) {
71 final XmlSchemaProvider provider
= XmlSchemaProvider
.getAvailableProvider(context
);
72 if (provider
== null) {
73 return Collections
.emptySet();
75 return provider
.getAvailableNamespaces(context
, tagName
);
78 public static Set
<String
> filterNamespaces(final Set
<String
> namespaces
, final String tagName
, final XmlFile context
) {
79 if (tagName
== null) {
82 final HashSet
<String
> set
= new HashSet
<String
>();
83 for (String namespace
: namespaces
) {
84 final XmlFile xmlFile
= XmlUtil
.findNamespace(context
, namespace
);
85 if (xmlFile
!= null) {
86 final XmlDocument document
= xmlFile
.getDocument();
87 assert document
!= null;
88 final XmlNSDescriptor nsDescriptor
= (XmlNSDescriptor
)document
.getMetaData();
89 assert nsDescriptor
!= null;
90 final XmlElementDescriptor
[] elementDescriptors
= nsDescriptor
.getRootElementsDescriptors(document
);
91 for (XmlElementDescriptor elementDescriptor
: elementDescriptors
) {
92 if (hasTag(elementDescriptor
, tagName
, new HashSet
<XmlElementDescriptor
>())) {
102 private static boolean hasTag(XmlElementDescriptor elementDescriptor
, String tagName
, Set
<XmlElementDescriptor
> visited
) {
103 final String name
= elementDescriptor
.getDefaultName();
104 if (name
.equals(tagName
)) {
107 for (XmlElementDescriptor descriptor
: elementDescriptor
.getElementsDescriptors(null)) {
108 if (!visited
.contains(elementDescriptor
)) {
109 visited
.add(elementDescriptor
);
110 if (hasTag(descriptor
, tagName
, visited
)) {
119 public Set
<String
> guessUnboundNamespaces(@NotNull final PsiElement element
, @NotNull XmlFile file
) {
120 if (!(element
instanceof XmlTag
)) {
121 return Collections
.emptySet();
123 final XmlTag tag
= (XmlTag
)element
;
124 final String name
= tag
.getLocalName();
125 final Set
<String
> byTagName
= getNamespacesByTagName(name
, file
);
126 if (!byTagName
.isEmpty()) {
127 byTagName
.removeAll(Arrays
.asList(tag
.knownNamespaces()));
130 final Set
<String
> set
= guessNamespace(file
, name
);
131 set
.removeAll(Arrays
.asList(tag
.knownNamespaces()));
133 final XmlTag parentTag
= tag
.getParentTag();
134 ns
: for (Iterator
<String
> i
= set
.iterator(); i
.hasNext();) {
135 final String s
= i
.next();
136 final Collection
<XmlFile
> namespaces
= XmlUtil
.findNSFilesByURI(s
, element
.getProject(), ModuleUtil
.findModuleForPsiElement(file
));
137 for (XmlFile namespace
: namespaces
) {
138 final XmlDocument document
= namespace
.getDocument();
139 assert document
!= null;
140 final XmlNSDescriptor nsDescriptor
= (XmlNSDescriptor
)document
.getMetaData();
141 assert nsDescriptor
!= null;
142 if (parentTag
!= null) {
145 final XmlElementDescriptor
[] descriptors
= nsDescriptor
.getRootElementsDescriptors(document
);
146 for (XmlElementDescriptor descriptor
: descriptors
) {
147 if (descriptor
.getName().equals(name
)) {
157 public void insertNamespaceDeclaration(@NotNull final XmlFile file
,
158 @NotNull final Editor editor
,
159 @NotNull final Set
<String
> possibleNamespaces
,
160 @Nullable String nsPrefix
,
161 @Nullable final Runner
<String
, IncorrectOperationException
> runAfter
) throws IncorrectOperationException
{
163 final String namespace
= possibleNamespaces
.iterator().next();
165 final Project project
= file
.getProject();
166 final XmlDocument document
= file
.getDocument();
167 assert document
!= null;
168 final XmlTag rootTag
= document
.getRootTag();
169 assert rootTag
!= null;
170 final XmlAttribute
[] attributes
= rootTag
.getAttributes();
171 XmlAttribute anchor
= null;
172 for (XmlAttribute attribute
: attributes
) {
173 final XmlAttributeDescriptor descriptor
= attribute
.getDescriptor();
174 if (attribute
.isNamespaceDeclaration() || (descriptor
!= null && descriptor
.isRequired())) {
181 final XmlSchemaProvider provider
= XmlSchemaProvider
.getAvailableProvider(file
);
182 String prefix
= nsPrefix
;
183 if (prefix
== null && provider
!= null) {
184 prefix
= provider
.getDefaultPrefix(namespace
, file
);
186 if (prefix
== null) {
189 final XmlElementFactory elementFactory
= XmlElementFactory
.getInstance(project
);
191 @NonNls final String qname
= "xmlns" + (prefix
.length() > 0 ?
":"+ prefix
:"");
192 final XmlAttribute attribute
= elementFactory
.createXmlAttribute(qname
, namespace
);
193 if (anchor
== null) {
194 rootTag
.add(attribute
);
196 rootTag
.addAfter(attribute
, anchor
);
199 String location
= null;
200 if (namespace
.length() > 0) {
201 if (provider
!= null) {
202 final Set
<String
> strings
= provider
.getLocations(namespace
, file
);
203 if (strings
!= null && strings
.size() > 0) {
204 location
= strings
.iterator().next();
209 if (location
!= null) {
210 XmlAttribute xmlAttribute
= rootTag
.getAttribute("xsi:schemaLocation");
211 final String pair
= namespace
+ " " + location
;
212 if (xmlAttribute
== null) {
213 xmlAttribute
= elementFactory
.createXmlAttribute("xsi:schemaLocation", pair
);
214 rootTag
.add(xmlAttribute
);
216 final String value
= xmlAttribute
.getValue();
217 if (!value
.contains(namespace
)) {
218 if (StringUtil
.isEmptyOrSpaces(value
)) {
219 xmlAttribute
.setValue(pair
);
221 xmlAttribute
.setValue(value
.trim() + " " + pair
);
226 CodeStyleManager
.getInstance(project
).reformat(rootTag
);
228 if (namespace
.length() == 0) {
229 final XmlAttribute xmlAttribute
= rootTag
.getAttribute(qname
);
230 if (xmlAttribute
!= null) {
231 final XmlAttributeValue value
= xmlAttribute
.getValueElement();
232 assert value
!= null;
233 final int startOffset
= value
.getTextOffset();
234 editor
.getCaretModel().moveToOffset(startOffset
);
237 if (runAfter
!= null) {
238 runAfter
.run(prefix
);
242 public boolean isPrefixDeclared(final XmlTag context
, String namespacePrefix
) {
243 @NonNls String nsDeclarationAttrName
= null;
244 for(XmlTag t
= context
; t
!= null; t
= t
.getParentTag()) {
245 if (t
.hasNamespaceDeclarations()) {
246 if (nsDeclarationAttrName
== null) nsDeclarationAttrName
= "xmlns:"+namespacePrefix
;
247 if (t
.getAttributeValue(nsDeclarationAttrName
) != null) return true;
253 private static Set
<String
> guessNamespace(final PsiFile file
, String tagName
) {
254 final Project project
= file
.getProject();
255 final Collection
<VirtualFile
> files
= XmlTagNamesIndex
.getFilesByTagName(tagName
, project
);
256 final Set
<String
> possibleUris
= new LinkedHashSet
<String
>(files
.size());
257 for (VirtualFile virtualFile
: files
) {
258 final String namespace
= XmlNamespaceIndex
.getNamespace(virtualFile
, project
);
259 if (namespace
!= null) {
260 possibleUris
.add(namespace
);