/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source;
import com.intellij.psi.CommonClassNames;
import com.intellij.util.CharTable;
import com.intellij.util.containers.OpenTHashSet;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.CharSequenceHashingStrategy;
import com.intellij.util.text.CharSequenceSubSequence;
import com.intellij.util.text.StringFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* @author max
*/
public class CharTableImpl implements CharTable {
private static final int INTERN_THRESHOLD = 40; // 40 or more characters long tokens won't be interned.
private static final CharSequenceHashingStrategy HASHER = new CharSequenceHashingStrategy();
private static final OpenTHashSet<CharSequence> STATIC_ENTRIES = newStaticSet();
private final OpenTHashSet<CharSequence> entries = new OpenTHashSet<CharSequence>(10, 0.9f, HASHER);
@NotNull
@Override
public CharSequence intern(@NotNull final CharSequence text) {
if (text.length() > INTERN_THRESHOLD) return createSequence(text);
return doIntern(text);
}
@NotNull
public CharSequence doIntern(@NotNull CharSequence text) {
CharSequence interned = getStaticInterned(text.toString());
if (interned != null) {
return interned;
}
synchronized(entries) {
interned = entries.get(text);
if (interned != null) {
return interned;
}
// We need to create separate string just to prevent referencing all character data when original is string or char sequence over string
final CharSequence entry = createSequence(text);
boolean added = entries.add(entry);
assert added;
return entry;
}
}
@NotNull
@Override
public CharSequence intern(@NotNull final CharSequence baseText, final int startOffset, final int endOffset) {
if (endOffset - startOffset == baseText.length()) return baseText;
return intern(new CharSequenceSubSequence(baseText, startOffset, endOffset));
}
@NotNull
private static String createSequence(@NotNull CharSequence text) {
char[] buf = new char[text.length()];
CharArrayUtil.getChars(text, buf, 0);
return StringFactory.createShared(buf); // this way the .toString() doesn't create another instance (as opposed to new CharArrayCharSequence())
}
@Nullable
public static CharSequence getStaticInterned(@NotNull String text) {
return STATIC_ENTRIES.get(text);
}
public static void staticIntern(@NotNull String text) {
synchronized(STATIC_ENTRIES) {
STATIC_ENTRIES.add(text);
}
}
private static OpenTHashSet<CharSequence> newStaticSet() {
final OpenTHashSet<CharSequence> r = new OpenTHashSet<CharSequence>(10, 0.9f, HASHER);
r.add("==" );
r.add("!=" );
r.add("||" );
r.add("++" );
r.add("--" );
r.add("<" );
r.add("<=" );
r.add("<<=" );
r.add("<<" );
r.add(">" );
r.add("&" );
r.add("&&" );
r.add("+=" );
r.add("-=" );
r.add("*=" );
r.add("/=" );
r.add("&=" );
r.add("|=" );
r.add("^=" );
r.add("%=" );
r.add("(" );
r.add(")" );
r.add("{" );
r.add("}" );
r.add("[" );
r.add("]" );
r.add(";" );
r.add("," );
r.add("..." );
r.add("." );
r.add("=" );
r.add("!" );
r.add("~" );
r.add("?" );
r.add(":" );
r.add("+" );
r.add("-" );
r.add("*" );
r.add("/" );
r.add("|" );
r.add("^" );
r.add("%" );
r.add("@" );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add(" " );
r.add("\n" );
r.add("\n " );
r.add("\n " );
r.add("\n " );
r.add("\n " );
r.add("\n " );
r.add("\n " );
r.add("\n " );
r.add("\n " );
r.add("<");
r.add(">");
r.add("</");
r.add("/>");
r.add("\"");
r.add("\'");
r.add("<![CDATA[");
r.add("]]>");
r.add("<!--");
r.add("-->");
r.add("<!DOCTYPE");
r.add("SYSTEM");
r.add("PUBLIC");
r.add("<?");
r.add("?>");
r.add("<%");
r.add("%>");
r.add("<%=");
r.add("<%@");
r.add("${");
r.add("");
return r;
}
static {
addStringsFromClassToStatics(CommonClassNames.class);
}
public static void addStringsFromClassToStatics(@NotNull Class aClass) {
for (Field field : aClass.getDeclaredFields()) {
if ((field.getModifiers() & Modifier.STATIC) == 0) continue;
if ((field.getModifiers() & Modifier.PUBLIC) == 0) continue;
String typeName;
try {
typeName = (String)field.get(null);
}
catch (Exception e) {
continue;
}
staticIntern(typeName);
}
}
}