IDEA-154578 Unable to save settings (please restart IntelliJ Idea)

This commit is contained in:
Dmitry Batrak
2016-04-13 10:27:23 +03:00
parent 94b60ae68a
commit 017a96da26
3 changed files with 75 additions and 3 deletions
@@ -33,6 +33,7 @@ import com.intellij.openapi.fileEditor.impl.text.CodeFoldingState;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
@@ -67,7 +68,8 @@ class DocumentFoldingInfo implements JDOMExternalizable, CodeFoldingState {
@NonNls private static final String EXPANDED_ATT = "expanded";
@NonNls private static final String MARKER_TAG = "marker";
@NonNls private static final String DATE_ATT = "date";
@NonNls private static final String PLACEHOLDER_ATT = "placeholder";
@NonNls private static final String PLACEHOLDER_OLD_ATT = "placeholder";
@NonNls private static final String PLACEHOLDER_ATT = "ph";
DocumentFoldingInfo(@NotNull Project project, @NotNull Document document) {
myProject = project;
@@ -274,7 +276,7 @@ class DocumentFoldingInfo implements JDOMExternalizable, CodeFoldingState {
String signature = Integer.valueOf(marker.getStartOffset()) + ":" + Integer.valueOf(marker.getEndOffset());
e.setAttribute(SIGNATURE_ATT, signature);
String placeHolderText = fi == null ? DEFAULT_PLACEHOLDER : fi.placeHolder;
e.setAttribute(PLACEHOLDER_ATT, placeHolderText);
e.setAttribute(PLACEHOLDER_ATT, StringUtil.escapeIllegalXmlChars(placeHolderText));
element.addContent(e);
}
}
@@ -331,7 +333,8 @@ class DocumentFoldingInfo implements JDOMExternalizable, CodeFoldingState {
if (start < 0 || end >= document.getTextLength() || start > end) continue;
RangeMarker marker = document.createRangeMarker(start, end);
myRangeMarkers.add(marker);
String placeHolderText = e.getAttributeValue(PLACEHOLDER_ATT);
String placeHolderText = StringUtil.unescapeIllegalXmlChars(e.getAttributeValue(PLACEHOLDER_ATT));
if (placeHolderText == null) e.getAttributeValue(PLACEHOLDER_OLD_ATT);
if (placeHolderText == null) placeHolderText = DEFAULT_PLACEHOLDER;
FoldingInfo fi = new FoldingInfo(placeHolderText, expanded);
marker.putUserData(FOLDING_INFO_KEY, fi);
@@ -24,6 +24,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.CharSequenceSubSequence;
import com.intellij.util.text.StringFactory;
import org.jdom.Verifier;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -2194,6 +2195,64 @@ public class StringUtil extends StringUtilRt {
}
}
/**
* Some characters are illegal in XML even as numerical character references. This method performs escaping of them
* in a custom format, which is supposed to be unescaped on retrieving from XML using {@link #unescapeIllegalXmlChars(String)}.
* Resulting text can be part of XML version 1.0 document.
*
* @see <a href="https://www.w3.org/International/questions/qa-controls">https://www.w3.org/International/questions/qa-controls</a>
* @see Verifier#isXMLCharacter(int)
*/
public static String escapeIllegalXmlChars(@NotNull String text) {
StringBuilder b = null;
int lastPos = 0;
for (int i = 0; i < text.length(); i++) {
int c = text.codePointAt(i);
if (Character.isSupplementaryCodePoint(c)) {
//noinspection AssignmentToForLoopParameter
i++;
}
if (c == '#' || !Verifier.isXMLCharacter(c)) {
if (b == null) b = new StringBuilder(text.length() + 5); // assuming there's one 'large' char (e.g. 0xFFFF) to escape numerically
b.append(text, lastPos, i).append('#');
if (c != '#') b.append(Integer.toHexString(c));
b.append('#');
lastPos = i + 1;
}
}
return b == null ? text : b.append(text, lastPos, text.length()).toString();
}
/**
* @see #escapeIllegalXmlChars(String)
*/
public static String unescapeIllegalXmlChars(@NotNull String text) {
StringBuilder b = null;
int lastPos = 0;
for (int i = 0; i < text.length(); i++) {
int c = text.charAt(i);
if (c == '#') {
int numberEnd = text.indexOf('#', i + 1);
if (numberEnd > 0) {
int charCode;
try {
charCode = numberEnd == (i + 1) ? '#' : Integer.parseInt(text.substring(i + 1, numberEnd), 16);
}
catch (NumberFormatException e) {
continue;
}
if (b == null) b = new StringBuilder(text.length());
b.append(text, lastPos, i);
b.append((char) charCode);
//noinspection AssignmentToForLoopParameter
i = numberEnd;
lastPos = i + 1;
}
}
}
return b == null ? text : b.append(text, lastPos, text.length()).toString();
}
public static void quote(@NotNull final StringBuilder builder) {
quote(builder, '\"');
}
@@ -19,6 +19,7 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.LineSeparator;
import com.intellij.xml.util.XmlStringUtil;
import org.jdom.Verifier;
import org.junit.Test;
import java.nio.CharBuffer;
@@ -492,4 +493,13 @@ public class StringUtilTest {
assertEquals(3, StringUtil.lastIndexOf("axaxa", 'x', 0, 5));
assertEquals(2, StringUtil.lastIndexOf("abcd", 'c', -42, 99)); // #IDEA-144968
}
@Test
public void testEscapingIllegalXmlChars() {
for (String s : new String[]{"ab\n\0\r\tde", "\\abc\1\2\3\uFFFFdef"}) {
String escapedText = StringUtil.escapeIllegalXmlChars(s);
assertNull(Verifier.checkCharacterData(escapedText));
assertEquals(s, StringUtil.unescapeIllegalXmlChars(escapedText));
}
}
}