/* * Copyright 2013-2017 consulo.io * * 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 consulo.csharp.lang.psi.impl.source.injection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.TextRange; import com.intellij.psi.LiteralTextEscaper; import com.intellij.psi.PsiLanguageInjectionHost; /** * @author VISTALL * @since 31.08.14 * <p/> * Based on java impl */ public class CSharpStringLiteralEscaper<T extends PsiLanguageInjectionHost> extends LiteralTextEscaper<T> { public static boolean parseStringCharacters(@NotNull String chars, @NotNull StringBuilder outChars, @Nullable int[] sourceOffsets) { assert sourceOffsets == null || sourceOffsets.length == chars.length() + 1; if(chars.indexOf('\\') < 0) { outChars.append(chars); if(sourceOffsets != null) { for(int i = 0; i < sourceOffsets.length; i++) { sourceOffsets[i] = i; } } return true; } int index = 0; final int outOffset = outChars.length(); while(index < chars.length()) { char c = chars.charAt(index++); if(sourceOffsets != null) { sourceOffsets[outChars.length() - outOffset] = index - 1; sourceOffsets[outChars.length() + 1 - outOffset] = index; } if(c != '\\') { outChars.append(c); continue; } if(index == chars.length()) { return false; } c = chars.charAt(index++); switch(c) { case 'b': outChars.append('\b'); break; case 't': outChars.append('\t'); break; case 'n': outChars.append('\n'); break; case 'f': outChars.append('\f'); break; case 'r': outChars.append('\r'); break; case '"': outChars.append('"'); break; case '\'': outChars.append('\''); break; case '\\': outChars.append('\\'); break; 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; case 'u': // uuuuu1234 is valid too while(index != chars.length() && chars.charAt(index) == 'u') { index++; } if(index + 4 <= chars.length()) { try { int code = Integer.parseInt(chars.substring(index, index + 4), 16); //line separators are invalid here if(code == 0x000a || code == 0x000d) { return false; } c = chars.charAt(index); if(c == '+' || c == '-') { return false; } outChars.append((char) code); index += 4; } catch(Exception e) { return false; } } else { return false; } break; default: return false; } if(sourceOffsets != null) { sourceOffsets[outChars.length() - outOffset] = index; } } return true; } private int[] myOutSourceOffsets; public CSharpStringLiteralEscaper(@NotNull T host) { super(host); } @Override public boolean decode(@NotNull final TextRange rangeInsideHost, @NotNull StringBuilder outChars) { ProperTextRange.assertProperRange(rangeInsideHost); String subText = rangeInsideHost.substring(myHost.getText()); myOutSourceOffsets = new int[subText.length() + 1]; return parseStringCharacters(subText, outChars, myOutSourceOffsets); } @Override public int getOffsetInHost(int offsetInDecoded, @NotNull final TextRange rangeInsideHost) { int result = offsetInDecoded < myOutSourceOffsets.length ? myOutSourceOffsets[offsetInDecoded] : -1; if(result == -1) { return -1; } return (result <= rangeInsideHost.getLength() ? result : rangeInsideHost.getLength()) + rangeInsideHost.getStartOffset(); } @Override public boolean isOneLine() { return true; } }