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
.project
.Project
;
8 import com
.intellij
.openapi
.util
.TextRange
;
9 import com
.intellij
.psi
.PsiDocumentManager
;
10 import com
.intellij
.psi
.PsiElement
;
11 import com
.intellij
.psi
.PsiFile
;
12 import com
.intellij
.psi
.util
.PsiTreeUtil
;
13 import com
.intellij
.psi
.xml
.*;
14 import com
.intellij
.util
.ReflectionCache
;
15 import com
.intellij
.util
.ReflectionUtil
;
16 import com
.intellij
.util
.SmartList
;
17 import com
.intellij
.util
.xml
.reflect
.DomAttributeChildDescription
;
18 import com
.intellij
.util
.xml
.reflect
.DomCollectionChildDescription
;
19 import com
.intellij
.util
.xml
.reflect
.DomFixedChildDescription
;
20 import com
.intellij
.util
.xml
.reflect
.DomGenericInfo
;
21 import org
.jetbrains
.annotations
.NonNls
;
22 import org
.jetbrains
.annotations
.NotNull
;
23 import org
.jetbrains
.annotations
.Nullable
;
25 import java
.lang
.reflect
.Method
;
26 import java
.lang
.reflect
.Type
;
27 import java
.lang
.reflect
.TypeVariable
;
33 public class DomUtil
{
34 public static final TypeVariable
<Class
<GenericValue
>> GENERIC_VALUE_TYPE_VARIABLE
= ReflectionCache
.getTypeParameters(GenericValue
.class)[0];
39 public static Class
extractParameterClassFromGenericType(Type type
) {
40 return getGenericValueParameter(type
);
43 public static boolean isGenericValueType(Type type
) {
44 return getGenericValueParameter(type
) != null;
48 public static <T
extends DomElement
> T
findByName(@NotNull Collection
<T
> list
, @NonNls @NotNull String name
) {
49 for (T element
: list
) {
50 String elementName
= element
.getGenericInfo().getElementName(element
);
51 if (elementName
!= null && elementName
.equals(name
)) {
59 public static String
[] getElementNames(@NotNull Collection
<?
extends DomElement
> list
) {
60 ArrayList
<String
> result
= new ArrayList
<String
>(list
.size());
61 if (list
.size() > 0) {
62 for (DomElement element
: list
) {
63 String name
= element
.getGenericInfo().getElementName(element
);
69 return result
.toArray(new String
[result
.size()]);
73 public static List
<XmlTag
> getElementTags(@NotNull Collection
<?
extends DomElement
> list
) {
74 ArrayList
<XmlTag
> result
= new ArrayList
<XmlTag
>(list
.size());
75 for (DomElement element
: list
) {
76 XmlTag tag
= element
.getXmlTag();
85 public static XmlTag
[] getElementTags(@NotNull DomElement
[] list
) {
86 XmlTag
[] result
= new XmlTag
[list
.length
];
88 for (DomElement element
: list
) {
89 XmlTag tag
= element
.getXmlTag();
98 public static List
<JavaMethod
> getFixedPath(DomElement element
) {
99 assert element
.isValid();
100 final LinkedList
<JavaMethod
> methods
= new LinkedList
<JavaMethod
>();
102 final DomElement parent
= element
.getParent();
103 if (parent
instanceof DomFileElement
) {
106 final JavaMethod method
= getGetterMethod(element
, parent
);
107 if (method
== null) {
110 methods
.addFirst(method
);
111 element
= element
.getParent();
117 private static JavaMethod
getGetterMethod(final DomElement element
, final DomElement parent
) {
118 final String xmlElementName
= element
.getXmlElementName();
119 final String namespace
= element
.getXmlElementNamespaceKey();
120 final DomGenericInfo genericInfo
= parent
.getGenericInfo();
122 if (element
instanceof GenericAttributeValue
) {
123 final DomAttributeChildDescription description
= genericInfo
.getAttributeChildDescription(xmlElementName
, namespace
);
124 assert description
!= null;
125 return description
.getGetterMethod();
128 final DomFixedChildDescription description
= genericInfo
.getFixedChildDescription(xmlElementName
, namespace
);
129 return description
!= null ? description
.getGetterMethod(description
.getValues(parent
).indexOf(element
)) : null;
132 public static Class
getGenericValueParameter(Type type
) {
133 return ReflectionUtil
.substituteGenericType(GENERIC_VALUE_TYPE_VARIABLE
, type
);
137 public static XmlElement
getValueElement(GenericDomValue domValue
) {
138 if (domValue
instanceof GenericAttributeValue
) {
139 final GenericAttributeValue value
= (GenericAttributeValue
)domValue
;
140 final XmlAttributeValue attributeValue
= value
.getXmlAttributeValue();
141 return attributeValue
== null ? value
.getXmlAttribute() : attributeValue
;
143 return domValue
.getXmlTag();
147 public static List
<?
extends DomElement
> getIdentitySiblings(DomElement element
) {
148 final Method nameValueMethod
= ElementPresentationManager
.findNameValueMethod(element
.getClass());
149 if (nameValueMethod
!= null) {
150 final NameValue nameValue
= DomReflectionUtil
.findAnnotationDFS(nameValueMethod
, NameValue
.class);
151 if (nameValue
== null || nameValue
.unique()) {
152 final String stringValue
= ElementPresentationManager
.getElementName(element
);
153 if (stringValue
!= null) {
154 final DomElement parent
= element
.getManager().getIdentityScope(element
);
155 final DomGenericInfo domGenericInfo
= parent
.getGenericInfo();
156 final String tagName
= element
.getXmlElementName();
157 final DomCollectionChildDescription childDescription
= domGenericInfo
.getCollectionChildDescription(tagName
, element
.getXmlElementNamespaceKey());
158 if (childDescription
!= null) {
159 final ArrayList
<DomElement
> list
= new ArrayList
<DomElement
>(childDescription
.getValues(parent
));
160 list
.remove(element
);
166 return Collections
.emptyList();
169 public static <T
> List
<T
> getChildrenOfType(@NotNull final DomElement parent
, final Class
<T
> type
) {
170 final List
<T
> result
= new SmartList
<T
>();
171 parent
.acceptChildren(new DomElementVisitor() {
172 public void visitDomElement(final DomElement element
) {
173 if (type
.isInstance(element
)) {
174 result
.add((T
)element
);
181 public static <T
> List
<T
> getDefinedChildrenOfType(@NotNull final DomElement parent
, final Class
<T
> type
) {
182 final List
<T
> result
= new SmartList
<T
>();
183 parent
.acceptChildren(new DomElementVisitor() {
184 public void visitDomElement(final DomElement element
) {
185 if (type
.isInstance(element
) && element
.getXmlElement() != null) {
186 result
.add((T
)element
);
194 public static DomElement
findDuplicateNamedValue(DomElement element
, String newName
) {
195 return ElementPresentationManager
.findByName(getIdentitySiblings(element
), newName
);
198 public static boolean isAncestor(@NotNull DomElement ancestor
, @NotNull DomElement descendant
, boolean strict
) {
199 if (!strict
&& ancestor
.equals(descendant
)) return true;
200 final DomElement parent
= descendant
.getParent();
201 return parent
!= null && isAncestor(ancestor
, parent
, false);
204 public static void acceptAvailableChildren(final DomElement element
, final DomElementVisitor visitor
) {
205 final XmlTag tag
= element
.getXmlTag();
207 for (XmlTag xmlTag
: tag
.getSubTags()) {
208 final DomElement childElement
= element
.getManager().getDomElement(xmlTag
);
209 if (childElement
!= null) {
210 childElement
.accept(visitor
);
216 public static Collection
<Class
> getAllInterfaces(final Class aClass
, final Collection
<Class
> result
) {
217 final Class
[] interfaces
= ReflectionCache
.getInterfaces(aClass
);
218 result
.addAll(Arrays
.asList(interfaces
));
219 if (aClass
.getSuperclass() != null) {
220 getAllInterfaces(aClass
.getSuperclass(), result
);
222 for (Class anInterface
: interfaces
) {
223 getAllInterfaces(anInterface
, result
);
229 public static <T
> T
getParentOfType(final DomElement element
, final Class
<T
> requiredClass
, final boolean strict
) {
230 for (DomElement curElement
= strict
&& element
!= null? element
.getParent() : element
;
232 curElement
= curElement
.getParent()) {
233 if (requiredClass
.isInstance(curElement
)) {
234 return (T
)curElement
;
241 public static DomElement
getContextElement(@Nullable final Editor editor
) {
242 if(editor
== null) return null;
244 final Project project
= editor
.getProject();
245 if (project
== null) return null;
247 final PsiFile file
= PsiDocumentManager
.getInstance(project
).getPsiFile(editor
.getDocument());
248 if (!(file
instanceof XmlFile
)) {
252 return getDomElement(file
.findElementAt(editor
.getCaretModel().getOffset()));
256 public static DomElement
getDomElement(final Editor editor
, final PsiFile file
) {
257 return getDomElement(file
.findElementAt(editor
.getCaretModel().getOffset()));
261 public static DomElement
getDomElement(@Nullable final PsiElement element
) {
262 if (element
== null) return null;
264 final Project project
= element
.getProject();
265 final DomManager domManager
= DomManager
.getDomManager(project
);
266 final XmlAttribute attr
= PsiTreeUtil
.getParentOfType(element
, XmlAttribute
.class, false);
268 final GenericAttributeValue value
= domManager
.getDomElement(attr
);
269 if (value
!= null) return value
;
272 XmlTag tag
= PsiTreeUtil
.getParentOfType(element
, XmlTag
.class, false);
273 while (tag
!= null) {
274 final DomElement domElement
= domManager
.getDomElement(tag
);
275 if(domElement
!= null) return domElement
;
277 tag
= tag
.getParentTag();
283 public static <T
extends DomElement
> T
getOriginalElement(@NotNull final T domElement
) {
284 final XmlElement psiElement
= domElement
.getXmlElement();
285 if (psiElement
== null) return domElement
;
287 final PsiFile psiFile
= psiElement
.getContainingFile();
288 final PsiFile originalFile
= psiFile
.getOriginalFile();
289 if (originalFile
== null) return domElement
;
290 final TextRange range
= psiElement
.getTextRange();
291 final PsiElement element
= originalFile
.findElementAt(range
.getStartOffset());
292 final int maxLength
= range
.getLength();
293 final boolean isAttribute
= psiElement
instanceof XmlAttribute
;
294 final Class
<?
extends XmlElement
> clazz
= isAttribute ? XmlAttribute
.class : XmlTag
.class;
295 final DomManager domManager
= domElement
.getManager();
296 DomElement current
= null;
297 for (XmlElement next
= PsiTreeUtil
.getParentOfType(element
, clazz
, false);
298 next
!= null && next
.getTextLength() <= maxLength
;
299 next
= PsiTreeUtil
.getParentOfType(next
, clazz
, true)) {
300 current
= isAttribute? domManager
.getDomElement((XmlAttribute
)next
) : domManager
.getDomElement((XmlTag
)next
);
301 if (current
!= null && domElement
.getClass() != current
.getClass()) current
= null;