2 * Copyright (c) 2005 Your Corporation. All Rights Reserved.
4 package com
.intellij
.util
.xml
;
6 import com
.intellij
.openapi
.editor
.Editor
;
7 import com
.intellij
.openapi
.progress
.ProgressManager
;
8 import com
.intellij
.openapi
.project
.Project
;
9 import com
.intellij
.openapi
.util
.TextRange
;
10 import com
.intellij
.psi
.PsiDocumentManager
;
11 import com
.intellij
.psi
.PsiElement
;
12 import com
.intellij
.psi
.PsiFile
;
13 import com
.intellij
.psi
.util
.PsiTreeUtil
;
14 import com
.intellij
.psi
.xml
.*;
15 import com
.intellij
.util
.ArrayUtil
;
16 import com
.intellij
.util
.ReflectionCache
;
17 import com
.intellij
.util
.ReflectionUtil
;
18 import com
.intellij
.util
.SmartList
;
19 import com
.intellij
.util
.containers
.ContainerUtil
;
20 import com
.intellij
.util
.xml
.reflect
.DomAttributeChildDescription
;
21 import com
.intellij
.util
.xml
.reflect
.DomCollectionChildDescription
;
22 import com
.intellij
.util
.xml
.reflect
.DomFixedChildDescription
;
23 import com
.intellij
.util
.xml
.reflect
.DomGenericInfo
;
24 import org
.jetbrains
.annotations
.NonNls
;
25 import org
.jetbrains
.annotations
.NotNull
;
26 import org
.jetbrains
.annotations
.Nullable
;
28 import java
.lang
.reflect
.Method
;
29 import java
.lang
.reflect
.Type
;
30 import java
.lang
.reflect
.TypeVariable
;
36 public class DomUtil
{
37 public static final TypeVariable
<Class
<GenericValue
>> GENERIC_VALUE_TYPE_VARIABLE
= ReflectionCache
.getTypeParameters(GenericValue
.class)[0];
42 public static Class
extractParameterClassFromGenericType(Type type
) {
43 return getGenericValueParameter(type
);
46 public static boolean isGenericValueType(Type type
) {
47 return getGenericValueParameter(type
) != null;
51 public static <T
extends DomElement
> T
findByName(@NotNull Collection
<T
> list
, @NonNls @NotNull String name
) {
52 for (T element
: list
) {
53 String elementName
= element
.getGenericInfo().getElementName(element
);
54 if (elementName
!= null && elementName
.equals(name
)) {
62 public static String
[] getElementNames(@NotNull Collection
<?
extends DomElement
> list
) {
63 ArrayList
<String
> result
= new ArrayList
<String
>(list
.size());
64 if (list
.size() > 0) {
65 for (DomElement element
: list
) {
66 String name
= element
.getGenericInfo().getElementName(element
);
72 return ArrayUtil
.toStringArray(result
);
76 public static List
<XmlTag
> getElementTags(@NotNull Collection
<?
extends DomElement
> list
) {
77 ArrayList
<XmlTag
> result
= new ArrayList
<XmlTag
>(list
.size());
78 for (DomElement element
: list
) {
79 XmlTag tag
= element
.getXmlTag();
88 public static XmlTag
[] getElementTags(@NotNull DomElement
[] list
) {
89 XmlTag
[] result
= new XmlTag
[list
.length
];
91 for (DomElement element
: list
) {
92 XmlTag tag
= element
.getXmlTag();
101 public static List
<JavaMethod
> getFixedPath(DomElement element
) {
102 assert element
.isValid();
103 final LinkedList
<JavaMethod
> methods
= new LinkedList
<JavaMethod
>();
105 final DomElement parent
= element
.getParent();
106 if (parent
instanceof DomFileElement
) {
109 final JavaMethod method
= getGetterMethod(element
, parent
);
110 if (method
== null) {
113 methods
.addFirst(method
);
114 element
= element
.getParent();
120 private static JavaMethod
getGetterMethod(final DomElement element
, final DomElement parent
) {
121 final String xmlElementName
= element
.getXmlElementName();
122 final String namespace
= element
.getXmlElementNamespaceKey();
123 final DomGenericInfo genericInfo
= parent
.getGenericInfo();
125 if (element
instanceof GenericAttributeValue
) {
126 final DomAttributeChildDescription description
= genericInfo
.getAttributeChildDescription(xmlElementName
, namespace
);
127 assert description
!= null;
128 return description
.getGetterMethod();
131 final DomFixedChildDescription description
= genericInfo
.getFixedChildDescription(xmlElementName
, namespace
);
132 return description
!= null ? description
.getGetterMethod(description
.getValues(parent
).indexOf(element
)) : null;
135 public static Class
getGenericValueParameter(Type type
) {
136 return ReflectionUtil
.substituteGenericType(GENERIC_VALUE_TYPE_VARIABLE
, type
);
140 public static XmlElement
getValueElement(GenericDomValue domValue
) {
141 if (domValue
instanceof GenericAttributeValue
) {
142 final GenericAttributeValue value
= (GenericAttributeValue
)domValue
;
143 final XmlAttributeValue attributeValue
= value
.getXmlAttributeValue();
144 return attributeValue
== null ? value
.getXmlAttribute() : attributeValue
;
146 return domValue
.getXmlTag();
150 public static List
<?
extends DomElement
> getIdentitySiblings(DomElement element
) {
151 final Method nameValueMethod
= ElementPresentationManager
.findNameValueMethod(element
.getClass());
152 if (nameValueMethod
!= null) {
153 final NameValue nameValue
= DomReflectionUtil
.findAnnotationDFS(nameValueMethod
, NameValue
.class);
154 if (nameValue
== null || nameValue
.unique()) {
155 final String stringValue
= ElementPresentationManager
.getElementName(element
);
156 if (stringValue
!= null) {
157 final DomElement parent
= element
.getManager().getIdentityScope(element
);
158 final DomGenericInfo domGenericInfo
= parent
.getGenericInfo();
159 final String tagName
= element
.getXmlElementName();
160 final DomCollectionChildDescription childDescription
= domGenericInfo
.getCollectionChildDescription(tagName
, element
.getXmlElementNamespaceKey());
161 if (childDescription
!= null) {
162 final ArrayList
<DomElement
> list
= new ArrayList
<DomElement
>(childDescription
.getValues(parent
));
163 list
.remove(element
);
169 return Collections
.emptyList();
172 public static <T
> List
<T
> getChildrenOfType(@NotNull final DomElement parent
, final Class
<T
> type
) {
173 final List
<T
> result
= new SmartList
<T
>();
174 parent
.acceptChildren(new DomElementVisitor() {
175 public void visitDomElement(final DomElement element
) {
176 if (type
.isInstance(element
)) {
177 result
.add((T
)element
);
184 public static List
<DomElement
> getDefinedChildren(@NotNull final DomElement parent
, final boolean tags
, final boolean attributes
) {
185 if (parent
instanceof MergedObject
) {
186 final SmartList
<DomElement
> result
= new SmartList
<DomElement
>();
187 parent
.acceptChildren(new DomElementVisitor() {
188 public void visitDomElement(final DomElement element
) {
189 if (element
.getXmlElement() != null) {
198 ProgressManager
.getInstance().checkCanceled();
200 if (parent
instanceof GenericAttributeValue
) return Collections
.emptyList();
202 if (parent
instanceof DomFileElement
) {
203 final DomFileElement element
= (DomFileElement
)parent
;
204 return tags ? Arrays
.asList(element
.getRootElement()) : Collections
.<DomElement
>emptyList();
207 final XmlElement xmlElement
= parent
.getXmlElement();
208 if (xmlElement
instanceof XmlTag
) {
209 XmlTag tag
= (XmlTag
) xmlElement
;
210 final DomManager domManager
= parent
.getManager();
211 final SmartList
<DomElement
> result
= new SmartList
<DomElement
>();
213 for (final XmlAttribute attribute
: tag
.getAttributes()) {
214 ContainerUtil
.addIfNotNull(domManager
.getDomElement(attribute
), result
);
218 for (final XmlTag subTag
: tag
.getSubTags()) {
219 ContainerUtil
.addIfNotNull(domManager
.getDomElement(subTag
), result
);
224 return Collections
.emptyList();
227 public static <T
> List
<T
> getDefinedChildrenOfType(@NotNull final DomElement parent
, final Class
<T
> type
, boolean tags
, boolean attributes
) {
228 return ContainerUtil
.findAll(getDefinedChildren(parent
, tags
, attributes
), type
);
231 public static <T
> List
<T
> getDefinedChildrenOfType(@NotNull final DomElement parent
, final Class
<T
> type
) {
232 return getDefinedChildrenOfType(parent
, type
, true, true);
236 public static DomElement
findDuplicateNamedValue(DomElement element
, String newName
) {
237 return ElementPresentationManager
.findByName(getIdentitySiblings(element
), newName
);
240 public static boolean isAncestor(@NotNull DomElement ancestor
, @NotNull DomElement descendant
, boolean strict
) {
241 if (!strict
&& ancestor
.equals(descendant
)) return true;
242 final DomElement parent
= descendant
.getParent();
243 return parent
!= null && isAncestor(ancestor
, parent
, false);
246 public static void acceptAvailableChildren(final DomElement element
, final DomElementVisitor visitor
) {
247 final XmlTag tag
= element
.getXmlTag();
249 for (XmlTag xmlTag
: tag
.getSubTags()) {
250 final DomElement childElement
= element
.getManager().getDomElement(xmlTag
);
251 if (childElement
!= null) {
252 childElement
.accept(visitor
);
258 public static Collection
<Class
> getAllInterfaces(final Class aClass
, final Collection
<Class
> result
) {
259 final Class
[] interfaces
= ReflectionCache
.getInterfaces(aClass
);
260 result
.addAll(Arrays
.asList(interfaces
));
261 if (aClass
.getSuperclass() != null) {
262 getAllInterfaces(aClass
.getSuperclass(), result
);
264 for (Class anInterface
: interfaces
) {
265 getAllInterfaces(anInterface
, result
);
271 public static <T
> T
getParentOfType(final DomElement element
, final Class
<T
> requiredClass
, final boolean strict
) {
272 for (DomElement curElement
= strict
&& element
!= null? element
.getParent() : element
;
274 curElement
= curElement
.getParent()) {
275 if (requiredClass
.isInstance(curElement
)) {
276 return (T
)curElement
;
283 public static <T
> T
getContextElement(@Nullable final Editor editor
, Class
<T
> clazz
) {
284 final DomElement element
= getContextElement(editor
);
285 return getParentOfType(element
, clazz
, false);
289 public static DomElement
getContextElement(@Nullable final Editor editor
) {
290 if(editor
== null) return null;
292 final Project project
= editor
.getProject();
293 if (project
== null) return null;
295 final PsiFile file
= PsiDocumentManager
.getInstance(project
).getPsiFile(editor
.getDocument());
296 if (!(file
instanceof XmlFile
)) {
300 return getDomElement(file
.findElementAt(editor
.getCaretModel().getOffset()));
304 public static DomElement
getDomElement(final Editor editor
, final PsiFile file
) {
305 return getDomElement(file
.findElementAt(editor
.getCaretModel().getOffset()));
309 public static DomElement
getDomElement(@Nullable final PsiElement element
) {
310 if (element
== null) return null;
312 final Project project
= element
.getProject();
313 final DomManager domManager
= DomManager
.getDomManager(project
);
314 final XmlAttribute attr
= PsiTreeUtil
.getParentOfType(element
, XmlAttribute
.class, false);
316 final GenericAttributeValue value
= domManager
.getDomElement(attr
);
317 if (value
!= null) return value
;
320 XmlTag tag
= PsiTreeUtil
.getParentOfType(element
, XmlTag
.class, false);
321 while (tag
!= null) {
322 final DomElement domElement
= domManager
.getDomElement(tag
);
323 if(domElement
!= null) return domElement
;
325 tag
= tag
.getParentTag();
331 public static <T
extends DomElement
> T
getOriginalElement(@NotNull final T domElement
) {
332 final XmlElement psiElement
= domElement
.getXmlElement();
333 if (psiElement
== null) return domElement
;
335 final PsiFile psiFile
= psiElement
.getContainingFile();
336 final PsiFile originalFile
= psiFile
.getOriginalFile();
337 if (originalFile
== null) return domElement
;
338 final TextRange range
= psiElement
.getTextRange();
339 final PsiElement element
= originalFile
.findElementAt(range
.getStartOffset());
340 final int maxLength
= range
.getLength();
341 final boolean isAttribute
= psiElement
instanceof XmlAttribute
;
342 final Class
<?
extends XmlElement
> clazz
= isAttribute ? XmlAttribute
.class : XmlTag
.class;
343 final DomManager domManager
= domElement
.getManager();
344 DomElement current
= null;
345 for (XmlElement next
= PsiTreeUtil
.getParentOfType(element
, clazz
, false);
346 next
!= null && next
.getTextLength() <= maxLength
;
347 next
= PsiTreeUtil
.getParentOfType(next
, clazz
, true)) {
348 current
= isAttribute? domManager
.getDomElement((XmlAttribute
)next
) : domManager
.getDomElement((XmlTag
)next
);
349 if (current
!= null && domElement
.getClass() != current
.getClass()) current
= null;
354 public static <T
extends DomElement
> T
addElementAfter(@NotNull final T anchor
) {
355 final DomElement parent
= anchor
.getParent();
356 final DomCollectionChildDescription childDescription
= (DomCollectionChildDescription
)anchor
.getChildDescription();
357 assert parent
!= null;
358 final List
<?
extends DomElement
> list
= childDescription
.getValues(parent
);
359 final int i
= list
.indexOf(anchor
);
361 return (T
)childDescription
.addValue(parent
, i
+ 1);
365 public static <T
extends DomElement
> T
findDomElement(@Nullable final PsiElement element
, final Class
<T
> beanClass
) {
366 if (element
== null) return null;
368 XmlTag tag
= PsiTreeUtil
.getParentOfType(element
, XmlTag
.class);
369 DomElement domElement
;
371 while (tag
!= null) {
372 domElement
= DomManager
.getDomManager(tag
.getProject()).getDomElement(tag
);
374 if (domElement
!= null) {
375 return domElement
.getParentOfType(beanClass
, false);
377 tag
= tag
.getParentTag();