garbage reduced
[fedora-idea.git] / dom / openapi / src / com / intellij / util / xml / DomUtil.java
blob56f15fe8bb3dfd1eaca953b9bc322a7edc0a180a
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.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;
31 import java.util.*;
33 /**
34 * @author peter
36 public class DomUtil {
37 public static final TypeVariable<Class<GenericValue>> GENERIC_VALUE_TYPE_VARIABLE = ReflectionCache.getTypeParameters(GenericValue.class)[0];
39 private DomUtil() {
42 public static Class extractParameterClassFromGenericType(Type type) {
43 return getGenericValueParameter(type);
46 public static boolean isGenericValueType(Type type) {
47 return getGenericValueParameter(type) != null;
50 @Nullable
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)) {
55 return element;
58 return null;
61 @NotNull
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);
67 if (name != null) {
68 result.add(name);
72 return ArrayUtil.toStringArray(result);
75 @NotNull
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();
80 if (tag != null) {
81 result.add(tag);
84 return result;
87 @NotNull
88 public static XmlTag[] getElementTags(@NotNull DomElement[] list) {
89 XmlTag[] result = new XmlTag[list.length];
90 int i = 0;
91 for (DomElement element: list) {
92 XmlTag tag = element.getXmlTag();
93 if (tag != null) {
94 result[i++] = tag;
97 return result;
100 @Nullable
101 public static List<JavaMethod> getFixedPath(DomElement element) {
102 assert element.isValid();
103 final LinkedList<JavaMethod> methods = new LinkedList<JavaMethod>();
104 while (true) {
105 final DomElement parent = element.getParent();
106 if (parent instanceof DomFileElement) {
107 break;
109 final JavaMethod method = getGetterMethod(element, parent);
110 if (method == null) {
111 return null;
113 methods.addFirst(method);
114 element = element.getParent();
116 return methods;
119 @Nullable
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);
139 @Nullable
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;
145 } else {
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);
164 return list;
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);
181 return result;
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) {
190 result.add(element);
194 return result;
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>();
212 if (attributes) {
213 for (final XmlAttribute attribute : tag.getAttributes()) {
214 ContainerUtil.addIfNotNull(domManager.getDomElement(attribute), result);
217 if (tags) {
218 for (final XmlTag subTag : tag.getSubTags()) {
219 ContainerUtil.addIfNotNull(domManager.getDomElement(subTag), result);
222 return 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);
235 @Nullable
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();
248 if (tag != null) {
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);
267 return result;
270 @Nullable
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;
273 curElement != null;
274 curElement = curElement.getParent()) {
275 if (requiredClass.isInstance(curElement)) {
276 return (T)curElement;
279 return null;
282 @Nullable
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);
288 @Nullable
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)) {
297 return null;
300 return getDomElement(file.findElementAt(editor.getCaretModel().getOffset()));
303 @Nullable
304 public static DomElement getDomElement(final Editor editor, final PsiFile file) {
305 return getDomElement(file.findElementAt(editor.getCaretModel().getOffset()));
308 @Nullable
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);
315 if (attr != null) {
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();
327 return null;
330 @NotNull
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;
351 return (T)current;
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);
360 assert i >= 0;
361 return (T)childDescription.addValue(parent, i + 1);
364 @Nullable
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();
379 return null;