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