
package com.intellij.lexer;

import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.text.CharArrayCharSequence;
import com.intellij.util.text.CharArrayUtil;

/**
 * Used to process scriptlet code in JSP attribute values like this:
 *   attribute="<%=texts.get(\"Blabla\")%>"
 */
public class EscapedJavaLexer extends LexerBase {
  private char mySurroundingQuote;
  private final JavaLexer myJavaLexer;

  private CharSequence myBuffer;
  private int myBufferEnd;
  private int myCurOffset;
  private IElementType myTokenType = null;
  private int myTokenEnd;

  public EscapedJavaLexer(char surroundingQuote, LanguageLevel languageLevel) {
    mySurroundingQuote = surroundingQuote;
    myJavaLexer = new JavaLexer(languageLevel);
  }

  public char getSurroundingQuote() {
    return mySurroundingQuote;
  }

  public void setSurroundingQuote(char surroundingQuote) {
    mySurroundingQuote = surroundingQuote;
  }

  public void start(CharSequence buffer, int startOffset, int endOffset, int state) {
    myBuffer = buffer;
    myCurOffset = startOffset;
    myTokenEnd = startOffset;
    myBufferEnd = endOffset;
    myTokenType = null;
  }

  public CharSequence getBufferSequence() {
    return myBuffer;
  }

  public void start(char[] buffer, int startOffset, int endOffset, int initialState) {
    start(new CharArrayCharSequence(buffer),startOffset,endOffset,0);
  }

  public int getState() {
    return 0;
  }

  public IElementType getTokenType() {
    locateToken();
    return myTokenType;
  }

  public final int getTokenStart(){
    locateToken();
    return myCurOffset;
  }

  public final int getTokenEnd(){
    locateToken();
    return myTokenEnd;
  }

  public final void advance(){
    locateToken();
    myTokenType = null;
    myCurOffset = myTokenEnd;
  }

  public final char[] getBuffer(){
    return CharArrayUtil.fromSequence(myBuffer);
  }

  public final int getBufferEnd(){
    return myBufferEnd;
  }

  private void locateToken() {
    if (myTokenType != null) return;
    if (myCurOffset >= myBufferEnd) return;

    boolean esc = false;
    int offset = myCurOffset;
    int state = 0; // 0 -- start/end
                   // 1 -- inside string
                   // 2 -- after escape (/) in string literal

    char literalStarter = 0;
    do {
      char c = myBuffer.charAt(offset);
      boolean wasEsc = esc;
      esc = false;
      if (c == '\\') {
        if (!wasEsc) {
          esc = true;
        }
        else {
          state = 2;
        }
      }
      else if (state == 0) {
        if (c == '\'' || c == '\"') {
          literalStarter = c;
          state = 1;
        }
      }
      else if (state == 1) {
        if (c == literalStarter) {
          state = 0;
          offset++;
          break;
        }
      }
      else if (state == 2) {
        state = 1;
      }

      if (!esc && state == 0 || offset + 1 >= myBufferEnd) {
        break;
      }

      offset++;
    }
    while (true);

    if(offset == myBufferEnd - 1) state = 0;
    switch (state){
      case 0:
        if(offset == myCurOffset){
          myJavaLexer.start(myBuffer, myCurOffset, myBufferEnd,0);
          myTokenType = myJavaLexer.getTokenType();
          myTokenEnd = myJavaLexer.getTokenEnd();
        }
        else {
          myTokenType = literalStarter == '\"' ? JavaTokenType.STRING_LITERAL : JavaTokenType.CHARACTER_LITERAL;
          myTokenEnd = offset;
        }
        break;

      case 1:
        myTokenType = literalStarter == '\"' ? JavaTokenType.STRING_LITERAL : JavaTokenType.CHARACTER_LITERAL;
        myTokenEnd = offset;
        break;

      default:
        myTokenType = JavaTokenType.BAD_CHARACTER;
        myTokenEnd = offset;
        break;
    }
  }
}
@@ -2374,16 +2374,57 @@
    do %7B%0A
+      if (offset %3E= myBufferEnd) break;%0A%0A
       ch
@@ -2999,37 +2999,8 @@
 == 0
- %7C%7C offset + 1 %3E= myBufferEnd
 ) %7B%0A
@@ -3074,25 +3074,25 @@
   if(offset 
-=
+%3E
 = myBufferEn

Fix tests