in src/main/java/org/apache/sling/scripting/javascript/io/EspReader.java [398:719]
private int doRead() throws IOException {
// we return out of the loop, if we find a character passing the filter
for (;;) {
// Get a character from the input, which may well have been
// injected using the unread() method
int c = input.read();
// catch EOF
if (c < 0) {
// if a template text line is still incomplete, inject
// proper line ending and continue until this has been returned
if (!lineStart && state == PARSE_STATE_ESP) {
doVerbatim("\");"); // line ending injection
lineStart = true; // mark the line having ended
continue; // let's start read the injection
}
return c; // return the marker, we're done
}
// Do the finite state machine
switch (state) {
// NOTE :
// - continue means ignore current character, read next
// - break means return current character
// Template text state - text is wrapped in out.write()
case PARSE_STATE_ESP:
if (c == '$') { // might start EL-like ECMA expr
int c2 = input.read();
if (c2 == '{') {
// ECMA expression ${ ... }
pushState(PARSE_STATE_ECMA_EXPR_COMPACT);
startWrite(null);
if (!lineStart) {
doVerbatim("\");");
}
continue;
}
input.unread(c2);
} else if (c == '<') { // might start ECMA code/expr, ESP comment or JSP comment
int c2 = input.read();
int c3 = input.read();
if (c2 == '%') {
// ECMA or JSP comment
if (c3 == '=') {
// ECMA expression <%= ... %>
pushState(PARSE_STATE_ECMA_EXPR);
startWrite(null);
if (!lineStart) {
doVerbatim("\");");
}
continue;
} else if (c3 == '-') {
// (Possible) JSP Comment <%-- ... --%>
int c4 = input.read();
if (c4 == '-') {
pushState(PARSE_STATE_JSP_COMMENT);
continue;
}
input.unread(c4);
}
// We only get here if we are sure about ECMA
// ECMA code <% ... %>
input.unread(c3);
pushState(PARSE_STATE_ECMA);
if (!lineStart) {
doVerbatim("\");");
}
continue;
}
// Nothing special, push back read ahead
input.unread(c3);
input.unread(c2);
// End of template text line
} else if (c == '\r' || c == '\n') {
String lineEnd; // will be injected
// Check for real CRLF
if (c == '\r') {
int c2 = input.read();
if (c2 != '\n') {
input.unread(c2);
lineEnd = "\\r";
} else {
lineEnd = "\\r\\n";
}
} else {
lineEnd = "\\n";
}
// Only write line ending if not empty
if (!lineStart) {
doVerbatim("\");\n");
doVerbatim(lineEnd);
lineStart = true;
} else { // if (lineEnd.length() > 1) {
// no matter what line ending we have, make it LF
doVerbatim("\");\n");
doVerbatim(lineEnd);
startWrite("\"");
}
continue;
// template text is wrapped with double quotes, which
// when occurring in the text must be escaped.
// We also escape the escape character..
} else if (c == '"' || c == '\\') {
doVerbatim(String.valueOf((char) c));
c = '\\';
}
// If in template text at the beginning of a line
if (lineStart) {
lineStart = false;
startWrite("\"" + (char) c);
continue;
}
break;
// Reading ECMA code or and ECMA expression
case PARSE_STATE_ECMA_EXPR:
case PARSE_STATE_ECMA:
if (c == '%') {
// might return to PARSE_STATE_ESP
int c2 = input.read();
if (c2 == '>') {
// An expression is wrapped in out.write()
if (popState() == PARSE_STATE_ECMA_EXPR) {
doVerbatim(");");
}
// next ESP needs out.write(
lineStart = true;
continue;
}
// false alert, push back
input.unread(c2);
} else if (c == '/') {
// might be ECMA Comment
int c2 = input.read();
if (c2 == '/') {
// single line comment
pushState(PARSE_STATE_ECMA_COMMENTL);
} else if (c2 == '*') {
// multiline comment
pushState(PARSE_STATE_ECMA_COMMENT);
}
// false alert, push back
input.unread(c2);
} else if (c == '\'' || c == '"') {
// an ECMA string
escape = false; // start unescaped
quoteChar = (char) c; // to recognize the end
pushState(PARSE_STATE_QUOTE);
}
break;
// reading compact (EL-like) ECMA Expression
case PARSE_STATE_ECMA_EXPR_COMPACT:
if (c == '}') { //might be the end of a compact expression
// An expression is wrapped in out.write()
popState();
doVerbatim(");");
// next ESP needs out.write(
lineStart = true;
continue;
}
break;
// Reading a JSP comment, only returning line endings
case PARSE_STATE_JSP_COMMENT:
// JSP comments end complexly with --%>
if (c == '-') {
int c2 = input.read();
if (c2 == '-') {
int c3 = input.read();
if (c3 == '%') {
int c4 = input.read();
if (c4 == '>') {
// we really reached the end ...
popState();
continue;
}
input.unread(c4);
}
input.unread(c3);
}
input.unread(c2);
// well, not definitely correct but reasonably accurate
// ;-)
} else if (c == '\r' || c == '\n') {
// terminate an open template line
if (!lineStart) {
input.unread(c); // push back the character
doVerbatim("\");"); // insert ");
lineStart = true; // mark the line start
continue; // Force read of the "
}
break;
}
// continue reading another character in the comment
continue;
// Read an ECMA string upto the ending quote character
case PARSE_STATE_QUOTE:
// if unescaped quote character
if (c == quoteChar && !escape) {
popState();
} else {
// mark escape - only if not already escaped (bug 7079)
escape = c == '\\' && !escape;
}
break;
// Return characters unfiltered
case PARSE_STATE_VERBATIM:
// Go back to previous state if all characters read
if (--verbatimChars < 0) {
popState();
}
break;
// Return an ECMA multiline comment, ending with */
case PARSE_STATE_ECMA_COMMENT:
// Might be the end of the comment
if (c == '*') {
int c2 = input.read();
if (c2 == '/') {
popState(); // back to previous
doVerbatim("/"); // return slash verbatim
} else {
input.unread(c2);
}
}
break;
// Return an ECMA single line comment, ending with end of line
case PARSE_STATE_ECMA_COMMENTL:
// CRLF recognition
if (c == '\r') {
int c2 = input.read();
if (c2 == '\n') {
popState();
}
input.unread(c2);
// LF only line end
} else if (c == '\n') {
popState();
}
break;
// What ???!!!
default:
// we warn and go back to default state
log.warn("doRead(): unknown state " + state);
state = PARSE_STATE_ESP;
break;
} // switch
// Exiting the switch normally we return the current character
return c;
} // for(;;)
}