XmlPropertiesFiles inserts property in alpha-order if it is possible

This commit is contained in:
Dmitry Batkovich
2015-02-11 15:22:45 +03:00
parent dfa7040f0a
commit 47e6e070ab
3 changed files with 145 additions and 26 deletions
@@ -33,7 +33,10 @@ import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.reference.SoftLazyValue;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
@@ -47,29 +50,54 @@ import java.util.*;
* Date: 7/26/11
*/
public class XmlPropertiesFileImpl extends XmlPropertiesFile {
private static final Key<CachedValue<PropertiesFile>> KEY = Key.create("xml properties file");
private final XmlFile myFile;
private final SoftLazyValue<MultiMap<String, IProperty>> myPropertiesMap = new SoftLazyValue<MultiMap<String, IProperty>>() {
private final SoftLazyValue<Info> myInfo = new SoftLazyValue<Info>() {
@NotNull
@Override
protected MultiMap<String, IProperty> compute() {
XmlTag rootTag = myFile.getRootTag();
if (rootTag == null) {
return MultiMap.emptyInstance();
}
XmlTag[] entries = rootTag.findSubTags("entry");
MultiMap<String, IProperty> map = new MultiMap<String, IProperty>();
for (XmlTag entry : entries) {
XmlProperty property = new XmlProperty(entry, XmlPropertiesFileImpl.this);
map.putValue(property.getKey(), property);
}
return map;
protected Info compute() {
return new Info();
}
};
private class Info {
private final MultiMap<String, IProperty> myPropertiesMap = MultiMap.create();
private List<IProperty> myPropertiesOrder;
private boolean mySorted;
public Info() {
XmlTag rootTag = myFile.getRootTag();
final List<IProperty> propertiesOrder = new ArrayList<IProperty>();
if (rootTag != null) {
XmlTag[] entries = rootTag.findSubTags("entry");
for (XmlTag entry : entries) {
XmlProperty property = new XmlProperty(entry, XmlPropertiesFileImpl.this);
propertiesOrder.add(property);
myPropertiesMap.putValue(property.getKey(), property);
}
}
mySorted = PropertiesImplUtil.isAlphaSorted(propertiesOrder);
myPropertiesOrder = mySorted ? propertiesOrder : null;
}
public void setSorted(boolean sorted) {
mySorted = sorted;
myPropertiesOrder = null;
}
public MultiMap<String, IProperty> getPropertiesMap() {
return myPropertiesMap;
}
public List<IProperty> getPropertiesOrder() {
return myPropertiesOrder;
}
public boolean isSorted() {
return mySorted;
}
}
private XmlPropertiesFileImpl(XmlFile file) {
myFile = file;
}
@@ -83,19 +111,19 @@ public class XmlPropertiesFileImpl extends XmlPropertiesFile {
@NotNull
@Override
public List<IProperty> getProperties() {
return new ArrayList<IProperty>(myPropertiesMap.getValue().values());
return new ArrayList<IProperty>(myInfo.getValue().getPropertiesMap().values());
}
@Override
public IProperty findPropertyByKey(@NotNull @NonNls String key) {
Collection<IProperty> properties = myPropertiesMap.getValue().get(key);
Collection<IProperty> properties = myInfo.getValue().getPropertiesMap().get(key);
return properties.isEmpty() ? null : properties.iterator().next();
}
@NotNull
@Override
public List<IProperty> findPropertiesByKey(@NotNull @NonNls String key) {
return new ArrayList<IProperty>(myPropertiesMap.getValue().get(key));
return new ArrayList<IProperty>(myInfo.getValue().getPropertiesMap().get(key));
}
@NotNull
@@ -122,22 +150,62 @@ public class XmlPropertiesFileImpl extends XmlPropertiesFile {
@Override
public IProperty addPropertyAfter(String key, String value, Property anchor) {
return null;
return addPropertyAfterAndCheckAlphaSorting(key, value, anchor, true, true);
}
@NotNull
public IProperty addPropertyAfterAndCheckAlphaSorting(String key, String value, @Nullable IProperty anchor, boolean addToEnd, boolean checkAlphaSorting) {
final XmlTag anchorTag = anchor == null ? null : (XmlTag)anchor.getPsiElement();
final XmlTag rootTag = myFile.getRootTag();
final XmlTag entry = createPropertyTag(key, value);
final XmlTag addedEntry = (XmlTag) (anchorTag == null ? myFile.getRootTag().addSubTag(entry, !addToEnd) : rootTag.addAfter(entry, anchorTag));
final XmlProperty property = new XmlProperty(addedEntry, this);
myInfo.getValue().getPropertiesMap().putValue(key, property);
if (checkAlphaSorting) {
checkAlphaSorting(property);
}
return property;
}
@NotNull
@Override
public IProperty addProperty(String key, String value) {
final XmlTag entry = createPropertyTag(key, value);
if (myInfo.getValue().isSorted()) {
final XmlProperty dummyProperty = new XmlProperty(entry, this);
final int insertIndex = Collections.binarySearch(myInfo.getValue().getPropertiesOrder(), dummyProperty, new Comparator<IProperty>() {
@Override
public int compare(IProperty p1, IProperty p2) {
final String k1 = p1.getKey();
final String k2 = p2.getKey();
return k1.compareTo(k2);
}
});
final IProperty insertPosition;
final IProperty inserted;
if (insertIndex == -1) {
inserted = addPropertyAfterAndCheckAlphaSorting(key, value, null, false, false);
myInfo.getValue().getPropertiesOrder().add(0, inserted);
}
else {
final int position = insertIndex < 0 ? -insertIndex - 2 : insertIndex;
insertPosition = myInfo.getValue().getPropertiesOrder().get(position);
inserted = addPropertyAfterAndCheckAlphaSorting(key, value, insertPosition, false, false);
myInfo.getValue().getPropertiesOrder().add(position + 1, inserted);
}
return inserted;
} else {
return addPropertyAfterAndCheckAlphaSorting(key, value, null, true, false);
}
}
private XmlTag createPropertyTag(final String key, final String value) {
XmlTag rootTag = myFile.getRootTag();
XmlTag entry = rootTag.createChildTag("entry", "", value, false);
entry.setAttribute("key", key);
rootTag.addSubTag(entry, false);
final XmlProperty property = new XmlProperty(entry, this);
myPropertiesMap.getValue().putValue(key, property);
return property;
return entry;
}
public static PropertiesFile getPropertiesFile(final PsiFile file) {
CachedValuesManager manager = CachedValuesManager.getManager(file.getProject());
if (file instanceof XmlFile) {
@@ -194,7 +262,7 @@ public class XmlPropertiesFileImpl extends XmlPropertiesFile {
@Override
public boolean isAlphaSorted() {
return PropertiesImplUtil.isAlphaSorted(getProperties());
return myInfo.getValue().isSorted();
}
@Override
@@ -213,4 +281,29 @@ public class XmlPropertiesFileImpl extends XmlPropertiesFile {
public int hashCode() {
return myFile.hashCode();
}
private void checkAlphaSorting(final IProperty property) {
if (myInfo.getValue().isSorted()) {
final String key = property.getKey();
final XmlTag prev = getSibling((XmlTag)property.getPsiElement(), true);
final String prevKey = prev == null ? null : new XmlProperty(prev, this).getKey();
if (prevKey != null && key != null && prevKey.compareTo(key) > 0) {
myInfo.getValue().setSorted(false);
} else {
final XmlTag next = getSibling((XmlTag)property.getPsiElement(), false);
final String nextKey = next == null ? null : new XmlProperty(next, this).getKey();
if (nextKey != null && key != null && nextKey.compareTo(key) < 0) {
myInfo.getValue().setSorted(false);
}
}
}
}
private static XmlTag getSibling(final XmlTag entry, final boolean prev) {
XmlTag sibling = (XmlTag)(prev ? entry.getPrevSibling() : entry.getNextSibling());
while (sibling != null && !"entry".equals(sibling.getName())) {
sibling = (XmlTag)(prev ? sibling.getPrevSibling() : sibling.getNextSibling());
}
return sibling;
}
}
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Hi</comment>
<entry key="c">bar</entry>
<entry key="m">baz</entry>
</properties>
@@ -9,6 +9,8 @@ import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.psi.PsiFile;
import com.intellij.testFramework.PlatformTestCase;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import java.util.List;
@@ -81,6 +83,23 @@ public class XmlPropertiesTest extends LightPlatformCodeInsightFixtureTestCase {
assertEquals("vvv", property2.getValue());
}
public void testAddPropertyInAlphaOrder() {
final PsiFile psiFile = myFixture.configureByFile("bar.xml");
final PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(psiFile);
assertNotNull(propertiesFile);
WriteCommandAction.runWriteCommandAction(getProject(), new Runnable() {
public void run() {
propertiesFile.addProperty("d", "vvv");
propertiesFile.addProperty("a", "vvv");
propertiesFile.addProperty("l", "vvv");
propertiesFile.addProperty("v", "vvv");
}
});
assertTrue(propertiesFile.isAlphaSorted());
assertTrue(PropertiesImplUtil.getPropertiesFile(psiFile).isAlphaSorted());
}
@Override
protected String getTestDataPath() {
return PluginPathManager.getPluginHomePath("properties") + "/testData/xml/";