IDEADEV-22222
[fedora-idea.git] / dom / openapi / src / com / intellij / util / xml / DomUtil.java
blob27298ee4f26a0ac778eaf74040f32d3ac38a574d
1 /*
2 * Copyright (c) 2005 Your Corporation. All Rights Reserved.
3 */
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;
28 import java.util.*;
30 /**
31 * @author peter
33 public class DomUtil {
34 public static final TypeVariable<Class<GenericValue>> GENERIC_VALUE_TYPE_VARIABLE = ReflectionCache.getTypeParameters(GenericValue.class)[0];
36 private DomUtil() {
39 public static Class extractParameterClassFromGenericType(Type type) {
40 return getGenericValueParameter(type);
43 public static boolean isGenericValueType(Type type) {
44 return getGenericValueParameter(type) != null;
47 @Nullable
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)) {
52 return element;
55 return null;
58 @NotNull
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);
64 if (name != null) {
65 result.add(name);
69 return result.toArray(new String[result.size()]);
72 @NotNull
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();
77 if (tag != null) {
78 result.add(tag);
81 return result;
84 @NotNull
85 public static XmlTag[] getElementTags(@NotNull DomElement[] list) {
86 XmlTag[] result = new XmlTag[list.length];
87 int i = 0;
88 for (DomElement element: list) {
89 XmlTag tag = element.getXmlTag();
90 if (tag != null) {
91 result[i++] = tag;
94 return result;
97 @Nullable
98 public static List<JavaMethod> getFixedPath(DomElement element) {
99 assert element.isValid();
100 final LinkedList<JavaMethod> methods = new LinkedList<JavaMethod>();
101 while (true) {
102 final DomElement parent = element.getParent();
103 if (parent instanceof DomFileElement) {
104 break;
106 final JavaMethod method = getGetterMethod(element, parent);
107 if (method == null) {
108 return null;
110 methods.addFirst(method);
111 element = element.getParent();
113 return methods;
116 @Nullable
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);
136 @Nullable
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;
142 } else {
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);
161 return list;
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);
178 return result;
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);
190 return result;
193 @Nullable
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();
206 if (tag != null) {
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);
225 return result;
228 @Nullable
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;
231 curElement != null;
232 curElement = curElement.getParent()) {
233 if (requiredClass.isInstance(curElement)) {
234 return (T)curElement;
237 return null;
240 @Nullable
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)) {
249 return null;
252 return getDomElement(file.findElementAt(editor.getCaretModel().getOffset()));
255 @Nullable
256 public static DomElement getDomElement(final Editor editor, final PsiFile file) {
257 return getDomElement(file.findElementAt(editor.getCaretModel().getOffset()));
260 @Nullable
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);
267 if (attr != null) {
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();
279 return null;
282 @NotNull
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;
303 return (T)current;