cleanup
[idea/community.git] / platform / core-impl / src / com / intellij / psi / impl / source / tree / LeafElement.java
1 /*
2  * Copyright 2000-2016 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
17 package com.intellij.psi.impl.source.tree;
18
19 import com.intellij.lang.ASTFactory;
20 import com.intellij.lang.ASTNode;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.util.Key;
23 import com.intellij.psi.PsiElement;
24 import com.intellij.psi.tree.IElementType;
25 import com.intellij.psi.tree.TokenSet;
26 import com.intellij.reference.SoftReference;
27 import com.intellij.util.IncorrectOperationException;
28 import com.intellij.util.text.CharArrayUtil;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31
32 public abstract class LeafElement extends TreeElement {
33   private static final Logger LOG = Logger.getInstance("com.intellij.psi.impl.source.tree.LeafElement");
34   private static final Key<SoftReference<String>> CACHED_TEXT = Key.create("CACHED_TEXT");
35
36   private static final int TEXT_MATCHES_THRESHOLD = 5;
37
38   private final CharSequence myText;
39
40   protected LeafElement(@NotNull IElementType type, CharSequence text) {
41     super(type);
42     myText = text;
43   }
44
45   @NotNull
46   @Override
47   public LeafElement clone() {
48     LeafElement clone = (LeafElement)super.clone();
49     clone.clearCaches();
50     return clone;
51   }
52
53   @Override
54   public int getTextLength() {
55     return myText.length();
56   }
57
58   @NotNull
59   @Override
60   public CharSequence getChars() {
61     return myText;
62   }
63
64   @NotNull
65   @Override
66   public String getText() {
67     CharSequence text = myText;
68     if (text.length() > 1000 && !(text instanceof String)) { // e.g. a large text file
69       String cachedText = SoftReference.dereference(getUserData(CACHED_TEXT));
70       if (cachedText == null) {
71         cachedText = text.toString();
72         putUserData(CACHED_TEXT, new SoftReference<String>(cachedText));
73       }
74       return cachedText;
75     }
76
77     return text.toString();
78   }
79
80   public char charAt(int position) {
81     return myText.charAt(position);
82   }
83
84   public int copyTo(@Nullable char[] buffer, int start) {
85     final int length = myText.length();
86     if (buffer != null) {
87       CharArrayUtil.getChars(myText, buffer, start, length);
88     }
89     return start + length;
90   }
91
92   @Override
93   @NotNull
94   public char[] textToCharArray() {
95     final char[] buffer = new char[myText.length()];
96     CharArrayUtil.getChars(myText, buffer, 0);
97     return buffer;
98   }
99
100   @Override
101   public boolean textContains(char c) {
102     final CharSequence text = myText;
103     final int len = text.length();
104
105     if (len > TEXT_MATCHES_THRESHOLD) {
106       char[] chars = CharArrayUtil.fromSequenceWithoutCopying(text);
107       if (chars != null) {
108         for (char aChar : chars) {
109           if (aChar == c) return true;
110         }
111         return false;
112       }
113     }
114
115     for (int i = 0; i < len; ++i) {
116       if (c == text.charAt(i)) return true;
117     }
118
119     return false;
120   }
121
122   @Override
123   protected int textMatches(@NotNull CharSequence buffer, int start) {
124     assert start >= 0 : start;
125     return leafTextMatches(myText, buffer, start);
126   }
127
128   static int leafTextMatches(@NotNull CharSequence text, @NotNull CharSequence buffer, int start) {
129     assert start >= 0 : start;
130     final int length = text.length();
131     if(buffer.length() - start < length) {
132       return start == 0 ? Integer.MIN_VALUE : -start;
133     }
134     for(int i = 0; i < length; i++){
135       int k = i + start;
136       if(text.charAt(i) != buffer.charAt(k)) {
137         return k == 0 ? Integer.MIN_VALUE : -k;
138       }
139     }
140     return start + length;
141   }
142
143   @NotNull
144   public LeafElement rawReplaceWithText(@NotNull String newText) {
145     LeafElement newLeaf = ASTFactory.leaf(getElementType(), newText);
146     copyUserDataTo(newLeaf);
147     rawReplaceWithList(newLeaf);
148     newLeaf.clearCaches();
149     return newLeaf;
150   }
151
152   @NotNull
153   public LeafElement replaceWithText(@NotNull String newText) {
154     LeafElement newLeaf = ChangeUtil.copyLeafWithText(this, newText);
155     getTreeParent().replaceChild(this, newLeaf);
156     return newLeaf;
157   }
158
159   @Override
160   public LeafElement findLeafElementAt(int offset) {
161     return this;
162   }
163
164   @Override
165   @SuppressWarnings("MethodOverloadsMethodOfSuperclass")
166   public boolean textMatches(@NotNull final CharSequence buf, int start, int end) {
167     final CharSequence text = getChars();
168     final int len = text.length();
169
170     if (end - start != len) return false;
171     if (buf == text) return true;
172
173     if (len > TEXT_MATCHES_THRESHOLD && text instanceof String && buf instanceof String) {
174       return ((String)text).regionMatches(0,(String)buf,start,len);
175     }
176
177     for (int i = 0; i < len; i++) {
178       if (text.charAt(i) != buf.charAt(start + i)) return false;
179     }
180
181     return true;
182   }
183
184   @Override
185   public void acceptTree(TreeElementVisitor visitor) {
186     visitor.visitLeaf(this);
187   }
188
189   @Override
190   public ASTNode findChildByType(IElementType type) {
191     return null;
192   }
193
194   @Override
195   public ASTNode findChildByType(IElementType type, @Nullable ASTNode anchor) {
196     return null;
197   }
198
199   @Override
200   @Nullable
201   public ASTNode findChildByType(@NotNull TokenSet typesSet) {
202     return null;
203   }
204
205   @Override
206   @Nullable
207   public ASTNode findChildByType(@NotNull TokenSet typesSet, @Nullable ASTNode anchor) {
208     return null;
209   }
210
211   @Override
212   public int hc() {
213     return leafHC(getChars());
214   }
215
216   static int leafHC(CharSequence text) {
217     final int len = text.length();
218     int hc = 0;
219
220     for (int i = 0; i < len; i++) {
221       hc += text.charAt(i);
222     }
223
224     return hc;
225   }
226
227   @Override
228   public TreeElement getFirstChildNode() {
229     return null;
230   }
231
232   @Override
233   public TreeElement getLastChildNode() {
234     return null;
235   }
236
237   @Override
238   public int getNotCachedLength() {
239     return myText.length();
240   }
241
242   @Override
243   public int getCachedLength() {
244     return getNotCachedLength();
245   }
246
247   @NotNull
248   @Override
249   public ASTNode[] getChildren(TokenSet filter) {
250     return EMPTY_ARRAY;
251   }
252
253   @Override
254   public void addChild(@NotNull ASTNode child, ASTNode anchorBefore) {
255     throw new IncorrectOperationException("Leaf elements cannot have children.");
256   }
257
258   @Override
259   public void addLeaf(@NotNull final IElementType leafType, final CharSequence leafText, final ASTNode anchorBefore) {
260     throw new IncorrectOperationException("Leaf elements cannot have children.");
261   }
262
263   @Override
264   public void addChild(@NotNull ASTNode child) {
265     throw new IncorrectOperationException("Leaf elements cannot have children.");
266   }
267
268   @Override
269   public void removeChild(@NotNull ASTNode child) {
270     throw new IncorrectOperationException("Leaf elements cannot have children.");
271   }
272
273   @Override
274   public void replaceChild(@NotNull ASTNode oldChild, @NotNull ASTNode newChild) {
275     throw new IncorrectOperationException("Leaf elements cannot have children.");
276   }
277
278   @Override
279   public void replaceAllChildrenToChildrenOf(ASTNode anotherParent) {
280     throw new IncorrectOperationException("Leaf elements cannot have children.");
281   }
282
283   @Override
284   public void removeRange(@NotNull ASTNode first, ASTNode firstWhichStayInTree) {
285     throw new IncorrectOperationException("Leaf elements cannot have children.");
286   }
287
288   @Override
289   public void addChildren(ASTNode firstChild, ASTNode lastChild, ASTNode anchorBefore) {
290     throw new IncorrectOperationException("Leaf elements cannot have children.");
291   }
292
293   @Override
294   public PsiElement getPsi() {
295     return null;
296   }
297
298   @Override
299   public <T extends PsiElement> T getPsi(@NotNull Class<T> clazz) {
300     return getPsi(clazz, getPsi(), LOG);
301   }
302
303   static <T extends PsiElement> T getPsi(@NotNull Class<T> clazz, PsiElement element, Logger log) {
304     log.assertTrue(clazz.isInstance(element), "unexpected psi class. expected: " + clazz
305                                              + " got: " + (element == null ? null : element.getClass()));
306     //noinspection unchecked
307     return (T)element;
308   }
309 }