4971ded53d23c614df4904f1bcfa3e0f40f51006
[idea/community.git] / xml / dom-impl / src / com / intellij / util / xml / impl / DomAnchorImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.util.xml.impl;
17
18 import com.intellij.psi.xml.XmlAttribute;
19 import com.intellij.psi.xml.XmlFile;
20 import com.intellij.psi.xml.XmlTag;
21 import com.intellij.util.xml.DomElement;
22 import com.intellij.util.xml.DomManager;
23 import com.intellij.util.xml.DomFileElement;
24 import com.intellij.util.xml.GenericAttributeValue;
25 import com.intellij.util.xml.reflect.AbstractDomChildrenDescription;
26 import com.intellij.openapi.diagnostic.Logger;
27 import org.jetbrains.annotations.Nullable;
28 import org.jetbrains.annotations.NotNull;
29
30 import java.util.List;
31
32 /**
33  * @author peter
34  */
35 public abstract class DomAnchorImpl<T extends DomElement> {
36   private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.impl.DomAnchorImpl");
37
38   public static <T extends DomElement> DomAnchorImpl<T> createAnchor(@NotNull T t) {
39     final DomElement parent = t.getParent();
40     if (parent == null) {
41       LOG.error("Parent null: " + t);
42     }
43
44     if (parent instanceof DomFileElementImpl) {
45       final DomFileElementImpl fileElement = (DomFileElementImpl)parent;
46       return new RootAnchor<T>(fileElement.getFile(), fileElement.getRootElementClass());
47     }
48
49     final DomAnchorImpl<DomElement> parentAnchor = createAnchor(parent);
50     final String name = t.getGenericInfo().getElementName(t);
51     final AbstractDomChildrenDescription description = t.getChildDescription();
52     if (name != null) {
53       return new NamedAnchor<T>(parentAnchor, description, name);
54     }
55
56     final List<? extends DomElement> values = description.getValues(parent);
57     final int index = values.indexOf(t);
58     if (index < 0) {
59       final XmlTag parentTag = parent.getXmlTag();
60       StringBuilder diag = new StringBuilder("Index<0: description=" + description + "\nparent=" + parent + "\nt=" + t + "\nvalues=" + values + "\n");
61       if (parentTag != null) {
62         diag.append("Parent tag: ").append(parentTag.getName()).append("\n");
63         if (t instanceof GenericAttributeValue) {
64           for (XmlAttribute attribute : parentTag.getAttributes()) {
65             diag.append("attr: ").append(attribute.getName());
66           }
67           diag.append("\n");
68         } else {
69           for (XmlTag tag : parentTag.getSubTags()) {
70             diag.append("subtag: ").append(tag.getName());
71           }
72           diag.append("\n");
73         }
74       }
75       diag.append("Child name: ").append(t.getXmlElementName()).append(";").append(t.getXmlElementNamespaceKey());
76       LOG.error(diag);
77     }
78     return new IndexedAnchor<T>(parentAnchor, description, index);
79   }
80
81   @Nullable
82   public abstract T retrieveDomElement();
83
84   @NotNull
85   public abstract XmlFile getContainingFile();
86
87   private static class NamedAnchor<T extends DomElement> extends DomAnchorImpl<T> {
88     private final DomAnchorImpl myParent;
89     private final AbstractDomChildrenDescription myDescr;
90     private final String myIndex;
91
92     private NamedAnchor(final DomAnchorImpl parent, final AbstractDomChildrenDescription descr, final String id) {
93       myParent = parent;
94       myDescr = descr;
95       myIndex = id;
96     }
97
98     @Override
99     public boolean equals(final Object o) {
100       if (this == o) return true;
101       if (!(o instanceof NamedAnchor)) return false;
102
103       final NamedAnchor that = (NamedAnchor)o;
104
105       if (myDescr != null ? !myDescr.equals(that.myDescr) : that.myDescr != null) return false;
106       if (myIndex != null ? !myIndex.equals(that.myIndex) : that.myIndex != null) return false;
107       if (myParent != null ? !myParent.equals(that.myParent) : that.myParent != null) return false;
108
109       return true;
110     }
111
112     @Override
113     public int hashCode() {
114       int result;
115       result = (myParent != null ? myParent.hashCode() : 0);
116       result = 31 * result + (myDescr != null ? myDescr.hashCode() : 0);
117       result = 31 * result + (myIndex != null ? myIndex.hashCode() : 0);
118       return result;
119     }
120
121     public T retrieveDomElement() {
122       final DomElement parent = myParent.retrieveDomElement();
123       if (parent == null) return null;
124
125       final List<? extends DomElement> list = myDescr.getValues(parent);
126       for (final DomElement element : list) {
127         final String s = element.getGenericInfo().getElementName(element);
128         if (myIndex.equals(s)) {
129           return (T)element;
130         }
131       }
132       return null;
133     }
134
135     @NotNull
136     public XmlFile getContainingFile() {
137       return myParent.getContainingFile();
138     }
139   }
140   private static class IndexedAnchor<T extends DomElement> extends DomAnchorImpl<T> {
141     private final DomAnchorImpl myParent;
142     private final AbstractDomChildrenDescription myDescr;
143     private final int myIndex;
144
145     private IndexedAnchor(final DomAnchorImpl parent, final AbstractDomChildrenDescription descr, final int index) {
146       myParent = parent;
147       myDescr = descr;
148       myIndex = index;
149     }
150
151     @Override
152     public boolean equals(final Object o) {
153       if (this == o) return true;
154       if (!(o instanceof IndexedAnchor)) return false;
155
156       final IndexedAnchor that = (IndexedAnchor)o;
157
158       if (myIndex != that.myIndex) return false;
159       if (myDescr != null ? !myDescr.equals(that.myDescr) : that.myDescr != null) return false;
160       if (myParent != null ? !myParent.equals(that.myParent) : that.myParent != null) return false;
161
162       return true;
163     }
164
165     @Override
166     public int hashCode() {
167       int result;
168       result = (myParent != null ? myParent.hashCode() : 0);
169       result = 31 * result + (myDescr != null ? myDescr.hashCode() : 0);
170       result = 31 * result + myIndex;
171       return result;
172     }
173
174     public T retrieveDomElement() {
175       final DomElement parent = myParent.retrieveDomElement();
176       if (parent == null) return null;
177
178       final List<? extends DomElement> list = myDescr.getValues(parent);
179       if (myIndex < 0 || myIndex >= list.size()) return null;
180
181       return (T)list.get(myIndex);
182     }
183
184     @NotNull
185     public XmlFile getContainingFile() {
186       return myParent.getContainingFile();
187     }
188   }
189
190   private static class RootAnchor<T extends DomElement> extends DomAnchorImpl<T> {
191     private final XmlFile myFile;
192     private final Class<T> myClass;
193
194     private RootAnchor(final XmlFile file, final Class<T> aClass) {
195       myFile = file;
196       myClass = aClass;
197     }
198
199     @Override
200     public boolean equals(final Object o) {
201       if (this == o) return true;
202       if (!(o instanceof RootAnchor)) return false;
203
204       final RootAnchor that = (RootAnchor)o;
205
206       if (myClass != null ? !myClass.equals(that.myClass) : that.myClass != null) return false;
207       if (myFile != null ? !myFile.equals(that.myFile) : that.myFile != null) return false;
208
209       return true;
210     }
211
212     @Override
213     public int hashCode() {
214       int result;
215       result = (myFile != null ? myFile.hashCode() : 0);
216       result = 31 * result + (myClass != null ? myClass.hashCode() : 0);
217       return result;
218     }
219
220     public T retrieveDomElement() {
221       final DomFileElement<T> fileElement = DomManager.getDomManager(myFile.getProject()).getFileElement(myFile, myClass);
222       return fileElement == null ? null : fileElement.getRootElement();
223     }
224
225     @NotNull
226     public XmlFile getContainingFile() {
227       return myFile;
228     }
229   }
230
231
232 }