0474126303516cc06aec062597263f4a21fdd43f
[fedora-idea.git] / platform / lang-impl / src / com / intellij / extapi / psi / ASTDelegatePsiElement.java
blob0474126303516cc06aec062597263f4a21fdd43f
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @author max
20 package com.intellij.extapi.psi;
22 import com.intellij.lang.ASTNode;
23 import com.intellij.lang.Language;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.util.Key;
26 import com.intellij.openapi.util.TextRange;
27 import com.intellij.psi.PsiElement;
28 import com.intellij.psi.PsiFile;
29 import com.intellij.psi.PsiInvalidElementAccessException;
30 import com.intellij.psi.impl.CheckUtil;
31 import com.intellij.psi.impl.PsiManagerEx;
32 import com.intellij.psi.impl.source.PsiFileImpl;
33 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
34 import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
35 import com.intellij.psi.impl.source.tree.ChangeUtil;
36 import com.intellij.psi.impl.source.tree.CompositeElement;
37 import com.intellij.psi.impl.source.tree.SharedImplUtil;
38 import com.intellij.psi.impl.source.tree.TreeElement;
39 import com.intellij.psi.tree.IElementType;
40 import com.intellij.psi.tree.TokenSet;
41 import com.intellij.util.Function;
42 import com.intellij.util.IncorrectOperationException;
43 import com.intellij.util.containers.ContainerUtil;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.List;
51 public abstract class ASTDelegatePsiElement extends PsiElementBase {
52 private static final Logger LOG = Logger.getInstance("#com.intellij.extapi.psi.ASTDelegatePsiElement");
54 private static final List EMPTY = Collections.emptyList();
56 public PsiManagerEx getManager() {
57 final PsiElement parent = getParent();
58 if (parent == null) throw new PsiInvalidElementAccessException(this);
59 return (PsiManagerEx)parent.getManager();
62 @NotNull
63 public PsiElement[] getChildren() {
64 PsiElement psiChild = getFirstChild();
65 if (psiChild == null) return EMPTY_ARRAY;
67 List<PsiElement> result = new ArrayList<PsiElement>();
68 while (psiChild != null) {
69 if (psiChild.getNode() instanceof CompositeElement) {
70 result.add(psiChild);
72 psiChild = psiChild.getNextSibling();
74 return result.toArray(new PsiElement[result.size()]);
77 public PsiElement getFirstChild() {
78 return SharedImplUtil.getFirstChild(getNode());
81 public PsiElement getLastChild() {
82 return SharedImplUtil.getLastChild(getNode());
85 public PsiElement getNextSibling() {
86 return SharedImplUtil.getNextSibling(getNode());
89 public PsiElement getPrevSibling() {
90 return SharedImplUtil.getPrevSibling(getNode());
93 public TextRange getTextRange() {
94 return getNode().getTextRange();
97 public int getStartOffsetInParent() {
98 return getNode().getStartOffset() - getNode().getTreeParent().getStartOffset();
101 public int getTextLength() {
102 return getNode().getTextLength();
105 public PsiElement findElementAt(int offset) {
106 ASTNode treeElement = getNode().findLeafElementAt(offset);
107 return SourceTreeToPsiMap.treeElementToPsi(treeElement);
110 public int getTextOffset() {
111 return getNode().getStartOffset();
114 public String getText() {
115 return getNode().getText();
118 @NotNull
119 public char[] textToCharArray() {
120 return getNode().getText().toCharArray();
123 public boolean textContains(char c) {
124 return getNode().textContains(c);
127 public <T> T getCopyableUserData(Key<T> key) {
128 return getNode().getCopyableUserData(key);
131 public <T> void putCopyableUserData(Key<T> key, T value) {
132 getNode().putCopyableUserData(key, value);
135 @NotNull
136 public abstract ASTNode getNode();
138 public void subtreeChanged() {
141 @NotNull
142 public Language getLanguage() {
143 return getNode().getElementType().getLanguage();
146 @Nullable
147 protected PsiElement findChildByType(IElementType type) {
148 ASTNode node = getNode().findChildByType(type);
149 return node == null ? null : node.getPsi();
152 protected PsiElement findChildByType(TokenSet type) {
153 ASTNode node = getNode().findChildByType(type);
154 return node == null ? null : node.getPsi();
157 @Nullable
158 protected PsiElement findChildByFilter(TokenSet tokenSet) {
159 ASTNode[] nodes = getNode().getChildren(tokenSet);
160 return nodes == null || nodes.length == 0 ? null : nodes[0].getPsi();
163 protected <T extends PsiElement> T[] findChildrenByType(IElementType elementType, Class<T> arrayClass) {
164 return ContainerUtil.map2Array(SharedImplUtil.getChildrenOfType(getNode(), elementType), arrayClass, new Function<ASTNode, T>() {
165 public T fun(final ASTNode s) {
166 return (T)s.getPsi();
171 protected <T extends PsiElement> List<T> findChildrenByType(TokenSet elementType) {
172 List<T> result = EMPTY;
173 ASTNode child = getNode().getFirstChildNode();
174 while (child != null) {
175 final IElementType tt = child.getElementType();
176 if (elementType.contains(tt)) {
177 if (result == EMPTY) {
178 result = new ArrayList<T>();
180 result.add((T)child.getPsi());
182 child = child.getTreeNext();
184 return result;
187 protected <T extends PsiElement> List<T> findChildrenByType(IElementType elementType) {
188 List<T> result = EMPTY;
189 ASTNode child = getNode().getFirstChildNode();
190 while (child != null) {
191 if (elementType == child.getElementType()) {
192 if (result == EMPTY) {
193 result = new ArrayList<T>();
195 result.add((T)child.getPsi());
197 child = child.getTreeNext();
199 return result;
202 protected <T extends PsiElement> T[] findChildrenByType(TokenSet elementType, Class<T> arrayClass) {
203 return (T[])ContainerUtil.map2Array(getNode().getChildren(elementType), arrayClass, new Function<ASTNode, PsiElement>() {
204 public PsiElement fun(final ASTNode s) {
205 return s.getPsi();
210 public PsiElement copy() {
211 return getNode().copyElement().getPsi();
214 public PsiElement add(@NotNull PsiElement element) throws IncorrectOperationException {
215 return addInnerBefore(element, null);
218 public PsiElement addBefore(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException {
219 return addInnerBefore(element, anchor);
222 private PsiElement addInnerBefore(final PsiElement element, final PsiElement anchor) throws IncorrectOperationException {
223 CheckUtil.checkWritable(this);
224 TreeElement elementCopy = ChangeUtil.copyToElement(element);
225 ASTNode treeElement = addInternal(elementCopy, elementCopy, SourceTreeToPsiMap.psiElementToTree(anchor), Boolean.TRUE);
226 if (treeElement != null) {
227 if (treeElement instanceof TreeElement) {
228 return ChangeUtil.decodeInformation((TreeElement) treeElement).getPsi();
230 return treeElement.getPsi();
232 throw new IncorrectOperationException("Element cannot be added");
235 public PsiElement addAfter(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException {
236 CheckUtil.checkWritable(this);
237 TreeElement elementCopy = ChangeUtil.copyToElement(element);
238 ASTNode treeElement = addInternal(elementCopy, elementCopy, SourceTreeToPsiMap.psiElementToTree(anchor), Boolean.FALSE);
239 if (treeElement instanceof TreeElement) {
240 return ChangeUtil.decodeInformation((TreeElement) treeElement).getPsi();
242 return treeElement.getPsi();
245 @Override
246 public void checkAdd(@NotNull final PsiElement element) throws IncorrectOperationException {
247 CheckUtil.checkWritable(this);
250 public ASTNode addInternal(ASTNode first, ASTNode last, ASTNode anchor, Boolean before) {
251 return CodeEditUtil.addChildren(getNode(), first, last, getAnchorNode(anchor, before));
254 @Override
255 public PsiElement addRange(final PsiElement first, final PsiElement last) throws IncorrectOperationException {
256 return SharedImplUtil.addRange(this, first, last, null, null);
259 @Override
260 public PsiElement addRangeBefore(@NotNull final PsiElement first, @NotNull final PsiElement last, final PsiElement anchor)
261 throws IncorrectOperationException {
262 return SharedImplUtil.addRange(this, first, last, SourceTreeToPsiMap.psiElementToTree(anchor), Boolean.TRUE);
265 @Override
266 public PsiElement addRangeAfter(final PsiElement first, final PsiElement last, final PsiElement anchor) throws IncorrectOperationException {
267 return SharedImplUtil.addRange(this, first, last, SourceTreeToPsiMap.psiElementToTree(anchor), Boolean.FALSE);
270 @Override
271 public void delete() throws IncorrectOperationException {
272 if (getParent() instanceof ASTDelegatePsiElement) {
273 CheckUtil.checkWritable(this);
274 ((ASTDelegatePsiElement) getParent()).deleteChildInternal(getNode());
276 else if (getParent() instanceof PsiFile) {
277 CheckUtil.checkWritable(this);
278 getParent().deleteChildRange(this, this);
280 else {
281 super.delete();
285 public void deleteChildInternal(@NotNull ASTNode child) {
286 CodeEditUtil.removeChild(getNode(), child);
289 @Override
290 public void checkDelete() throws IncorrectOperationException {
291 CheckUtil.checkWritable(this);
294 @Override
295 public void deleteChildRange(final PsiElement first, final PsiElement last) throws IncorrectOperationException {
296 CheckUtil.checkWritable(this);
297 ASTNode firstElement = SourceTreeToPsiMap.psiElementToTree(first);
298 ASTNode lastElement = SourceTreeToPsiMap.psiElementToTree(last);
300 LOG.assertTrue(firstElement.getTreeParent() == getNode());
301 LOG.assertTrue(lastElement.getTreeParent() == getNode());
302 CodeEditUtil.removeChildren(getNode(), firstElement, lastElement);
305 @Override
306 public PsiElement replace(@NotNull final PsiElement newElement) throws IncorrectOperationException {
307 if (getParent() instanceof ASTDelegatePsiElement) {
308 final ASTDelegatePsiElement parentElement = (ASTDelegatePsiElement)getParent();
309 CheckUtil.checkWritable(this);
310 TreeElement elementCopy = ChangeUtil.copyToElement(newElement);
311 parentElement.replaceChildInternal(this, elementCopy);
312 elementCopy = ChangeUtil.decodeInformation(elementCopy);
313 return SourceTreeToPsiMap.treeElementToPsi(elementCopy);
316 return super.replace(newElement);
319 public void replaceChildInternal(final PsiElement child, final TreeElement newElement) {
320 CodeEditUtil.replaceChild(getNode(), child.getNode(), newElement);
323 private ASTNode getAnchorNode(final ASTNode anchor, final Boolean before) {
324 ASTNode anchorBefore;
325 if (anchor != null) {
326 anchorBefore = before.booleanValue() ? anchor : anchor.getTreeNext();
328 else {
329 if (before != null && !before.booleanValue()) {
330 anchorBefore = getNode().getFirstChildNode();
332 else {
333 anchorBefore = null;
336 return anchorBefore;