// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.impl.source.resolve.reference.impl.manipulators;

import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;

/**
 * @author ven
 */
public class StringLiteralManipulator extends AbstractElementManipulator<PsiLiteralExpression> {
  @Override
  public PsiLiteralExpression handleContentChange(@NotNull PsiLiteralExpression expr, @NotNull TextRange range, String newContent) throws IncorrectOperationException {
    String oldText = expr.getText();
    if (oldText.startsWith("\"")) {
      newContent = StringUtil.escapeStringCharacters(newContent);
    }
    else if (oldText.startsWith("'") && newContent.length() <= 1) {
      newContent = newContent.length() == 1 && newContent.charAt(0) == '\''? "\\'" : newContent;
    }
    else {
      throw new IncorrectOperationException("cannot handle content change for: " + oldText + ", expr: " + expr);
    }

    String newText = oldText.substring(0, range.getStartOffset()) + newContent + oldText.substring(range.getEndOffset());
    final PsiExpression newExpr = JavaPsiFacade.getElementFactory(expr.getProject()).createExpressionFromText(newText, null);
    return (PsiLiteralExpression)expr.replace(newExpr);
  }

  @NotNull
  @Override
  public TextRange getRangeInElement(@NotNull final PsiLiteralExpression element) {
    return getValueRange(element);
  }

  @NotNull
  public static TextRange getValueRange(@NotNull PsiLiteralExpression element) {
    int length = element.getTextLength();
    boolean isQuoted;
    if (element instanceof PsiLiteralExpressionImpl) {
      // avoid calling getValue(): it allocates new string, it returns null for invalid escapes
      IElementType type = ((PsiLiteralExpressionImpl)element).getLiteralElementType();
      if (type == JavaTokenType.TEXT_BLOCK_LITERAL) {
        final String text = element.getText();
        int startOffset = findBlockStart(text);
        return startOffset < 0
               ? new TextRange(0, length)
               : new TextRange(startOffset, length - (text.endsWith("\"\"\"") ? 3 : 0));
      }
      isQuoted = type == JavaTokenType.STRING_LITERAL || type == JavaTokenType.CHARACTER_LITERAL;
    }
    else {
      Object value = element.getValue();
      isQuoted = value instanceof String || value instanceof Character;
    }
    return isQuoted ? new TextRange(1, Math.max(1, length - 1)) : TextRange.from(0, length);
  }

  private static int findBlockStart(String text) {
    if (!text.startsWith("\"\"\"")) return -1;
    final int length = text.length();
    for (int i = 3; i < length; i++) {
      final char c = text.charAt(i);
      if (c == '\n') return i + 1;
      if (!Character.isWhitespace(c)) return -1;
    }
    return -1;
  }
}
@@ -17,10 +17,10 @@
 0-20
-19
+20
  Jet
@@ -334,123 +334,8 @@
 .*;%0A
-import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;%0Aimport com.intellij.psi.tree.IElementType;%0A
 impo
@@ -1704,38 +1704,41 @@
 eralExpression e
-lement
+xpression
 ) %7B%0A    int leng
@@ -1739,30 +1739,33 @@
 t length = e
-lement
+xpression
 .getTextLeng
@@ -1778,314 +1778,36 @@
     
-boolean isQuoted;%0A    if (element instanceof PsiLiteralExpressionImpl) %7B%0A      // avoid calling getValue(): it allocates new string, it returns null for invalid escapes%0A      IElementType type = ((PsiLiteralExpressionImpl)element).getLiteralElementType();%0A      if (type == JavaTokenType.TEXT_BLOCK_LITERAL
+if (expression.isTextBlock()
 ) %7B%0A
@@ -1808,26 +1808,24 @@
 ()) %7B%0A      
-  
 final String
@@ -1829,30 +1829,33 @@
 ing text = e
-lement
+xpression
 .getText();%0A
@@ -1850,26 +1850,24 @@
 .getText();%0A
-  
       int st
@@ -1906,18 +1906,16 @@
 ;%0A      
-  
 return s
@@ -1929,18 +1929,16 @@
 set %3C 0%0A
-  
         
@@ -1982,18 +1982,16 @@
         
- 
- 
 : new Te
@@ -2064,163 +2064,167 @@
     
-  
 %7D%0A    
-  isQuoted = type == JavaTokenType.STRING_LITERAL %7C%7C type == JavaTokenType.CHARACTER_LITERAL;%0A    %7D%0A    else %7B%0A      Object value = element.getValu
+// avoid calling PsiLiteralExpression.getValue(): it allocates new string, it returns null for invalid escapes%0A    final PsiType type = expression.getTyp
 e();
@@ -2228,17 +2228,23 @@
 ();%0A    
- 
+boolean
  isQuote
@@ -2251,68 +2251,106 @@
 d = 
-value instanceof String %7C%7C value instanceof Character;%0A    %7D
+PsiType.CHAR.equals(type) %7C%7C type != null && type.equalsToText(CommonClassNames.JAVA_LANG_STRING);
 %0A   

use PsiLiteralExpression.isTextBlock() (IDEA-CR-60894)