package ee.edio.garmin;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.LiteralTextEscaper;
import com.intellij.psi.PsiLanguageInjectionHost;
import org.jetbrains.annotations.NotNull;
import java.util.Locale;
public class MonkeyStringLiteralEscaper<T extends PsiLanguageInjectionHost> extends LiteralTextEscaper<T> {
private int[] outSourceOffsets;
public MonkeyStringLiteralEscaper(@NotNull T host) {
super(host);
}
@Override
public boolean decode(@NotNull final TextRange rangeInsideHost, @NotNull final StringBuilder outChars) {
TextRange.assertProperRange(rangeInsideHost);
String subText = rangeInsideHost.substring(myHost.getText());
return parseStringCharacters(subText, outChars);
}
@Override
public int getOffsetInHost(final int offsetInDecoded, @NotNull final TextRange rangeInsideHost) {
TextRange.assertProperRange(rangeInsideHost);
int result = offsetInDecoded < outSourceOffsets.length ? outSourceOffsets[offsetInDecoded] : -1;
if (result == -1) return -1;
return (result <= rangeInsideHost.getLength() ? result : rangeInsideHost.getLength()) + rangeInsideHost.getStartOffset();
}
/**
* Escapes the specified string in accordance with https://golang.org/ref/spec#Rune_literals
*
* @param chars
* @param outChars
*/
public static void escapeString(@NotNull String chars, @NotNull StringBuilder outChars) {
int index = 0;
while (index < chars.length()) {
int c = chars.codePointAt(index);
switch (c) {
case (char) 7:
outChars.append("\\a");
break;
case '\b':
outChars.append("\\b");
break;
case '\f':
outChars.append("\\f");
break;
case '\n':
outChars.append("\\n");
break;
case '\r':
outChars.append("\\r");
break;
case '\t':
outChars.append("\\t");
break;
case (char) 0x0b:
outChars.append("\\v");
break;
case '\\':
outChars.append("\\\\");
break;
case '\'':
outChars.append("\\'");
break;
case '"':
outChars.append("\\\"");
break;
default:
switch (Character.getType(c)) {
case Character.CONTROL:
case Character.PRIVATE_USE:
case Character.UNASSIGNED:
if (c <= 0xffff) {
outChars.append("\\u").append(String.format(Locale.US, "%04X", c));
} else {
outChars.append("\\U").append(String.format(Locale.US, "%08X", c));
}
break;
default:
outChars.appendCodePoint(c);
}
}
index += Character.charCount(c);
}
}
private boolean parseStringCharacters(String chars, StringBuilder outChars) {
outSourceOffsets = new int[chars.length() + 1];
outSourceOffsets[chars.length()] = -1;
if (chars.indexOf('\\') < 0) {
outChars.append(chars);
for (int i = 0; i < outSourceOffsets.length; i++) {
outSourceOffsets[i] = i;
}
return true;
}
int index = 0;
while (index < chars.length()) {
char c = chars.charAt(index++);
outSourceOffsets[outChars.length()] = index - 1;
outSourceOffsets[outChars.length() + 1] = index;
if (c != '\\') {
outChars.append(c);
continue;
}
if (index == chars.length()) return false;
c = chars.charAt(index++);
switch (c) {
case 'a':
outChars.append((char) 7);
break;
case 'b':
outChars.append('\b');
break;
case 'f':
outChars.append('\f');
break;
case 'n':
outChars.append('\n');
break;
case 'r':
outChars.append('\r');
break;
case 't':
outChars.append('\t');
break;
case 'v':
outChars.append((char) 0x0b);
break;
case '\\':
outChars.append('\\');
break;
case '\'':
outChars.append('\'');
break;
case '"':
outChars.append('"');
break;
case '\n':
outChars.append('\n');
break;
// octal
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': {
char startC = c;
int v = (int) c - '0';
if (index < chars.length()) {
c = chars.charAt(index++);
if ('0' <= c && c <= '7') {
v <<= 3;
v += c - '0';
if (startC <= '3' && index < chars.length()) {
c = chars.charAt(index++);
if ('0' <= c && c <= '7') {
v <<= 3;
v += c - '0';
} else {
index--;
}
}
} else {
index--;
}
}
outChars.append((char) v);
}
break;
// hex
case 'x':
if (index + 2 <= chars.length()) {
try {
int v = Integer.parseInt(chars.substring(index, index + 2), 16);
outChars.append((char) v);
index += 2;
} catch (Exception e) {
return false;
}
} else {
return false;
}
break;
// little unicode
case 'u':
if (index + 4 <= chars.length()) {
try {
int v = Integer.parseInt(chars.substring(index, index + 4), 16);
c = chars.charAt(index);
if (c == '+' || c == '-') return false;
outChars.append((char) v);
index += 4;
} catch (Exception e) {
return false;
}
} else {
return false;
}
break;
// big unicode
case 'U':
if (index + 8 <= chars.length()) {
try {
int v = Integer.parseInt(chars.substring(index, index + 8), 16);
c = chars.charAt(index);
if (c == '+' || c == '-') return false;
outChars.append((char) v);
index += 8;
} catch (Exception e) {
return false;
}
} else {
return false;
}
break;
default:
return false;
}
outSourceOffsets[outChars.length()] = index;
}
return true;
}
@Override
public boolean isOneLine() {
return true;
}
}