more general replace() implementation
[fedora-idea.git] / platform / lang-impl / src / com / intellij / extapi / psi / ASTDelegatePsiElement.java
blob5dbb53923a0d656364a56cd119a9217cd8124e2c
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.SourceTreeToPsiMap;
33 import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
34 import com.intellij.psi.impl.source.tree.ChangeUtil;
35 import com.intellij.psi.impl.source.tree.CompositeElement;
36 import com.intellij.psi.impl.source.tree.SharedImplUtil;
37 import com.intellij.psi.impl.source.tree.TreeElement;
38 import com.intellij.psi.tree.IElementType;
39 import com.intellij.psi.tree.TokenSet;
40 import com.intellij.util.Function;
41 import com.intellij.util.IncorrectOperationException;
42 import com.intellij.util.containers.ContainerUtil;
43 import org.jetbrains.annotations.NotNull;
44 import org.jetbrains.annotations.Nullable;
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.List;
50 public abstract class ASTDelegatePsiElement extends PsiElementBase {
51 private static final Logger LOG = Logger.getInstance("#com.intellij.extapi.psi.ASTDelegatePsiElement");
53 private static final List EMPTY = Collections.emptyList();
55 public PsiManagerEx getManager() {
56 final PsiElement parent = getParent();
57 if (parent == null) throw new PsiInvalidElementAccessException(this);
58 return (PsiManagerEx)parent.getManager();
61 @NotNull
62 public PsiElement[] getChildren() {
63 PsiElement psiChild = getFirstChild();
64 if (psiChild == null) return EMPTY_ARRAY;
66 List<PsiElement> result = new ArrayList<PsiElement>();
67 while (psiChild != null) {
68 if (psiChild.getNode() instanceof CompositeElement) {
69 result.add(psiChild);
71 psiChild = psiChild.getNextSibling();
73 return result.toArray(new PsiElement[result.size()]);
76 public PsiElement getFirstChild() {
77 return SharedImplUtil.getFirstChild(getNode());
80 public PsiElement getLastChild() {
81 return SharedImplUtil.getLastChild(getNode());
84 public PsiElement getNextSibling() {
85 return SharedImplUtil.getNextSibling(getNode());
88 public PsiElement getPrevSibling() {
89 return SharedImplUtil.getPrevSibling(getNode());
92 public TextRange getTextRange() {
93 return getNode().getTextRange();
96 public int getStartOffsetInParent() {
97 return getNode().getStartOffset() - getNode().getTreeParent().getStartOffset();
100 public int getTextLength() {
101 return getNode().getTextLength();
104 public PsiElement findElementAt(int offset) {
105 ASTNode treeElement = getNode().findLeafElementAt(offset);
106 return SourceTreeToPsiMap.treeElementToPsi(treeElement);
109 public int getTextOffset() {
110 return getNode().getStartOffset();
113 public String getText() {
114 return getNode().getText();
117 @NotNull
118 public char[] textToCharArray() {
119 return getNode().getText().toCharArray();
122 public boolean textContains(char c) {
123 return getNode().textContains(c);
126 public <T> T getCopyableUserData(Key<T> key) {
127 return getNode().getCopyableUserData(key);
130 public <T> void putCopyableUserData(Key<T> key, T value) {
131 getNode().putCopyableUserData(key, value);
134 @NotNull
135 public abstract ASTNode getNode();
137 public void subtreeChanged() {
140 @NotNull
141 public Language getLanguage() {
142 return getNode().getElementType().getLanguage();
145 @Nullable
146 protected PsiElement findChildByType(IElementType type) {
147 ASTNode node = getNode().findChildByType(type);
148 return node == null ? null : node.getPsi();
151 protected PsiElement findChildByType(TokenSet type) {
152 ASTNode node = getNode().findChildByType(type);
153 return node == null ? null : node.getPsi();
156 @Nullable
157 protected PsiElement findChildByFilter(TokenSet tokenSet) {
158 ASTNode[] nodes = getNode().getChildren(tokenSet);
159 return nodes == null || nodes.length == 0 ? null : nodes[0].getPsi();
162 protected <T extends PsiElement> T[] findChildrenByType(IElementType elementType, Class<T> arrayClass) {
163 return ContainerUtil.map2Array(SharedImplUtil.getChildrenOfType(getNode(), elementType), arrayClass, new Function<ASTNode, T>() {
164 public T fun(final ASTNode s) {
165 return (T)s.getPsi();
170 protected <T extends PsiElement> List<T> findChildrenByType(TokenSet elementType) {
171 List<T> result = EMPTY;
172 ASTNode child = getNode().getFirstChildNode();
173 while (child != null) {
174 final IElementType tt = child.getElementType();
175 if (elementType.contains(tt)) {
176 if (result == EMPTY) {
177 result = new ArrayList<T>();
179 result.add((T)child.getPsi());
181 child = child.getTreeNext();
183 return result;
186 protected <T extends PsiElement> List<T> findChildrenByType(IElementType elementType) {
187 List<T> result = EMPTY;
188 ASTNode child = getNode().getFirstChildNode();
189 while (child != null) {
190 if (elementType == child.getElementType()) {
191 if (result == EMPTY) {
192 result = new ArrayList<T>();
194 result.add((T)child.getPsi());
196 child = child.getTreeNext();
198 return result;
201 protected <T extends PsiElement> T[] findChildrenByType(TokenSet elementType, Class<T> arrayClass) {
202 return (T[])ContainerUtil.map2Array(getNode().getChildren(elementType), arrayClass, new Function<ASTNode, PsiElement>() {
203 public PsiElement fun(final ASTNode s) {
204 return s.getPsi();
209 public PsiElement copy() {
210 return getNode().copyElement().getPsi();
213 public PsiElement add(@NotNull PsiElement element) throws IncorrectOperationException {
214 return addInnerBefore(element, null);
217 public PsiElement addBefore(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException {
218 return addInnerBefore(element, anchor);
221 private PsiElement addInnerBefore(final PsiElement element, final PsiElement anchor) throws IncorrectOperationException {
222 CheckUtil.checkWritable(this);
223 TreeElement elementCopy = ChangeUtil.copyToElement(element);
224 ASTNode treeElement = addInternal(elementCopy, elementCopy, SourceTreeToPsiMap.psiElementToTree(anchor), Boolean.TRUE);
225 if (treeElement != null) {
226 if (treeElement instanceof TreeElement) {
227 return ChangeUtil.decodeInformation((TreeElement) treeElement).getPsi();
229 return treeElement.getPsi();
231 throw new IncorrectOperationException("Element cannot be added");
234 public PsiElement addAfter(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException {
235 CheckUtil.checkWritable(this);
236 TreeElement elementCopy = ChangeUtil.copyToElement(element);
237 ASTNode treeElement = addInternal(elementCopy, elementCopy, SourceTreeToPsiMap.psiElementToTree(anchor), Boolean.FALSE);
238 if (treeElement instanceof TreeElement) {
239 return ChangeUtil.decodeInformation((TreeElement) treeElement).getPsi();
241 return treeElement.getPsi();
244 @Override
245 public void checkAdd(@NotNull final PsiElement element) throws IncorrectOperationException {
246 CheckUtil.checkWritable(this);
249 public ASTNode addInternal(ASTNode first, ASTNode last, ASTNode anchor, Boolean before) {
250 return CodeEditUtil.addChildren(getNode(), first, last, getAnchorNode(anchor, before));
253 @Override
254 public PsiElement addRange(final PsiElement first, final PsiElement last) throws IncorrectOperationException {
255 return SharedImplUtil.addRange(this, first, last, null, null);
258 @Override
259 public PsiElement addRangeBefore(@NotNull final PsiElement first, @NotNull final PsiElement last, final PsiElement anchor)
260 throws IncorrectOperationException {
261 return SharedImplUtil.addRange(this, first, last, SourceTreeToPsiMap.psiElementToTree(anchor), Boolean.TRUE);
264 @Override
265 public PsiElement addRangeAfter(final PsiElement first, final PsiElement last, final PsiElement anchor) throws IncorrectOperationException {
266 return SharedImplUtil.addRange(this, first, last, SourceTreeToPsiMap.psiElementToTree(anchor), Boolean.FALSE);
269 @Override
270 public void delete() throws IncorrectOperationException {
271 if (getParent() instanceof ASTDelegatePsiElement) {
272 CheckUtil.checkWritable(this);
273 ((ASTDelegatePsiElement) getParent()).deleteChildInternal(getNode());
275 else if (getParent() instanceof PsiFile) {
276 CheckUtil.checkWritable(this);
277 getParent().deleteChildRange(this, this);
279 else {
280 super.delete();
284 public void deleteChildInternal(@NotNull ASTNode child) {
285 CodeEditUtil.removeChild(getNode(), child);
288 @Override
289 public void checkDelete() throws IncorrectOperationException {
290 CheckUtil.checkWritable(this);
293 @Override
294 public void deleteChildRange(final PsiElement first, final PsiElement last) throws IncorrectOperationException {
295 CheckUtil.checkWritable(this);
296 ASTNode firstElement = SourceTreeToPsiMap.psiElementToTree(first);
297 ASTNode lastElement = SourceTreeToPsiMap.psiElementToTree(last);
299 LOG.assertTrue(firstElement.getTreeParent() == getNode());
300 LOG.assertTrue(lastElement.getTreeParent() == getNode());
301 CodeEditUtil.removeChildren(getNode(), firstElement, lastElement);
304 @Override
305 public PsiElement replace(@NotNull final PsiElement newElement) throws IncorrectOperationException {
306 CheckUtil.checkWritable(this);
307 TreeElement elementCopy = ChangeUtil.copyToElement(newElement);
308 if (getParent() instanceof ASTDelegatePsiElement) {
309 final ASTDelegatePsiElement parentElement = (ASTDelegatePsiElement)getParent();
310 parentElement.replaceChildInternal(this, elementCopy);
312 else {
313 CodeEditUtil.replaceChild(getParent().getNode(), getNode(), elementCopy);
315 elementCopy = ChangeUtil.decodeInformation(elementCopy);
316 return SourceTreeToPsiMap.treeElementToPsi(elementCopy);
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;