properties: remove useless caches in PropertiesImpl (memory consumption, typing performance)

This commit is contained in:
Dmitry Batkovich
2016-11-01 11:12:02 +03:00
parent 87b143438f
commit c7125938aa
2 changed files with 34 additions and 88 deletions
@@ -2472,7 +2472,7 @@ public class StringUtil extends StringUtilRt {
* Escape property name or key in property file. Unicode characters are escaped as well.
*
* @param input an input to escape
* @param isKey if true, the rules for key escaping are applied. The leading space is escaped in that case.
* @param isKey if true, the rules for key escaping are applied. All spaces are escaped in that case.
* @return an escaped string
*/
@NotNull
@@ -2483,7 +2483,7 @@ public class StringUtil extends StringUtilRt {
final char ch = input.charAt(i);
switch (ch) {
case ' ':
if (isKey && i == 0) {
if (isKey) {
// only the leading space has to be escaped
escaped.append('\\');
}
@@ -19,41 +19,39 @@ import com.intellij.extapi.psi.PsiFileBase;
import com.intellij.lang.ASTFactory;
import com.intellij.lang.ASTNode;
import com.intellij.lang.properties.*;
import com.intellij.lang.properties.ResourceBundle;
import com.intellij.lang.properties.parsing.PropertiesElementTypes;
import com.intellij.lang.properties.psi.PropertiesElementFactory;
import com.intellij.lang.properties.psi.PropertiesFile;
import com.intellij.lang.properties.psi.PropertiesList;
import com.intellij.lang.properties.psi.Property;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.tree.ChangeUtil;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.stubs.StubTree;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MostlySingularMultiMap;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PropertiesFileImpl extends PsiFileBase implements PropertiesFile {
private static final Logger LOG = Logger.getInstance(PropertiesFileImpl.class);
private static final TokenSet PROPERTIES_LIST_SET = TokenSet.create(PropertiesElementTypes.PROPERTIES_LIST);
private volatile MostlySingularMultiMap<String,IProperty> myPropertiesMap; //guarded by lock
private volatile List<IProperty> myProperties; //guarded by lock
private volatile boolean myAlphaSorted;
private final Object lock = new Object();
public PropertiesFileImpl(FileViewProvider viewProvider) {
super(viewProvider, PropertiesLanguage.INSTANCE);
@@ -73,65 +71,25 @@ public class PropertiesFileImpl extends PsiFileBase implements PropertiesFile {
@Override
@NotNull
public List<IProperty> getProperties() {
ensurePropertiesLoaded();
return myProperties;
final PropertiesList propertiesList = PsiTreeUtil.getStubChildOfType(this, PropertiesList.class);
if (propertiesList == null) return Collections.emptyList();
return Collections.unmodifiableList(PsiTreeUtil.getStubChildrenOfTypeAsList(propertiesList, Property.class));
}
private ASTNode getPropertiesList() {
return ArrayUtil.getFirstElement(getNode().getChildren(PROPERTIES_LIST_SET));
}
private void ensurePropertiesLoaded() {
if (myPropertiesMap != null) return;
final StubTree stubTree = getStubTree();
List<IProperty> properties;
MostlySingularMultiMap<String, IProperty> propertiesMap = new MostlySingularMultiMap<>();
if (stubTree != null) {
final PsiFileStub root = stubTree.getRoot();
final StubElement propertiesList = root.findChildStubByType(PropertiesElementTypes.PROPERTIES_LIST);
if (propertiesList != null) {
properties = Arrays.stream(propertiesList.getChildrenByType(PropertiesElementTypes.PROPERTY, Property[]::new))
.map(IProperty.class::cast)
.peek(p -> propertiesMap.add(p.getKey(), p))
.collect(Collectors.toList());
} else {
properties = Collections.emptyList();
}
} else {
final ASTNode[] props = getPropertiesList().getChildren(PropertiesElementTypes.PROPERTIES);
properties = new ArrayList<>(props.length);
for (final ASTNode prop : props) {
final Property property = (Property)prop.getPsi();
String key = property.getUnescapedKey();
propertiesMap.add(key, property);
properties.add(property);
}
}
final boolean isAlphaSorted = PropertiesImplUtil.isAlphaSorted(properties);
synchronized (lock) {
if (myPropertiesMap != null) return;
myProperties = properties;
myPropertiesMap = propertiesMap;
myAlphaSorted = isAlphaSorted;
}
}
@Nullable
@Override
public IProperty findPropertyByKey(@NotNull String key) {
ensurePropertiesLoaded();
synchronized (lock) {
Iterator<IProperty> iterator = myPropertiesMap.get(key).iterator();
return iterator.hasNext() ? iterator.next() : null;
}
return propertiesByKey(key).findFirst().orElse(null);
}
@Override
@NotNull
public List<IProperty> findPropertiesByKey(@NotNull String key) {
ensurePropertiesLoaded();
synchronized (lock) {
return ContainerUtil.collect(myPropertiesMap.get(key).iterator());
}
return propertiesByKey(key).collect(Collectors.toList());
}
@Override
@@ -161,25 +119,6 @@ public class PropertiesFileImpl extends PsiFileBase implements PropertiesFile {
return addPropertyAfter(property, position);
}
private IProperty findInsertionPosition(@NotNull IProperty property) {
synchronized (lock) {
ensurePropertiesLoaded();
if (myProperties.isEmpty()) {
return null;
}
if (myAlphaSorted) {
final int insertIndex = Collections.binarySearch(myProperties, property, (p1, p2) -> {
final String k1 = p1.getKey();
final String k2 = p2.getKey();
LOG.assertTrue(k1 != null && k2 != null);
return String.CASE_INSENSITIVE_ORDER.compare(k1, k2);
});
return insertIndex == -1 ? null :myProperties.get(insertIndex < 0 ? - insertIndex - 2 : insertIndex);
}
return myProperties.get(myProperties.size() - 1);
}
}
@Override
@NotNull
public PsiElement addPropertyAfter(@NotNull final IProperty property, @Nullable final IProperty anchor) throws IncorrectOperationException {
@@ -235,19 +174,26 @@ public class PropertiesFileImpl extends PsiFileBase implements PropertiesFile {
@Override
public boolean isAlphaSorted() {
synchronized (lock) {
ensurePropertiesLoaded();
return myAlphaSorted;
}
return PropertiesImplUtil.isAlphaSorted(getProperties());
}
@Override
public void clearCaches() {
super.clearCaches();
synchronized (lock) {
myPropertiesMap = null;
myProperties = null;
private IProperty findInsertionPosition(@NotNull IProperty property) {
List<IProperty> properties = getProperties();
if (properties.isEmpty()) return null;
if (PropertiesImplUtil.isAlphaSorted(properties)) {
final int insertIndex = Collections.binarySearch(getProperties(), property, (p1, p2) -> {
final String k1 = p1.getKey();
final String k2 = p2.getKey();
LOG.assertTrue(k1 != null && k2 != null);
return String.CASE_INSENSITIVE_ORDER.compare(k1, k2);
});
return insertIndex == -1 ? null : getProperties().get(insertIndex < 0 ? -insertIndex - 2 : insertIndex);
}
return ContainerUtil.getLastItem(properties);
}
private Stream<IProperty> propertiesByKey(@NotNull String key) {
final String escapedKey = StringUtil.escapeProperty(key, true);
return getProperties().stream().filter(p -> escapedKey.equals(p.getKey()));
}
}