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.
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();
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
) {
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();
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
);
135 public abstract ASTNode
getNode();
137 public void subtreeChanged() {
141 public Language
getLanguage() {
142 return getNode().getElementType().getLanguage();
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();
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();
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();
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
) {
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();
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
));
254 public PsiElement
addRange(final PsiElement first
, final PsiElement last
) throws IncorrectOperationException
{
255 return SharedImplUtil
.addRange(this, first
, last
, null, null);
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
);
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
);
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);
284 public void deleteChildInternal(@NotNull ASTNode child
) {
285 CodeEditUtil
.removeChild(getNode(), child
);
289 public void checkDelete() throws IncorrectOperationException
{
290 CheckUtil
.checkWritable(this);
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
);
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
);
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();
329 if (before
!= null && !before
.booleanValue()) {
330 anchorBefore
= getNode().getFirstChildNode();