/*
* © Copyright IBM Corp. 2013
*
* 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 nsf.playground.jsp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.faces.FacesException;
import com.ibm.commons.util.StringUtil;
import com.ibm.commons.util.TextUtil;
import com.ibm.xsp.FacesExceptionEx;
/**
* Tiny JSP processor that replaces basic JSP directives, <% %> <%=...%> and generates a Java class.
*
* @author priand
*/
public class JspCompiler {
public static final String START_TAG = "<%"; //$NON-NLS-1$
public static final String END_TAG = "%>"; //$NON-NLS-1$
public static final String START_DECLARATION = "<%!"; //$NON-NLS-1$
public static final String END_DECLARATION = "%>"; //$NON-NLS-1$
public static final String START_DIRECTIVE = "<%@"; //$NON-NLS-1$
public static final String END_DIRECTIVE = "%>"; //$NON-NLS-1$
private static final String IMPORTPATTERN = "<%@[ \\t\\n\\r]*page[ \\t\\n\\r]+import[ \\t\\n\\r]*=[ \\t\\n\\r]*[\\\'\\\"]([^\\\'\\\"]+)[\\\'\\\"][ \\t\\n\\r]*%>";
private static Pattern importPattern = Pattern.compile(IMPORTPATTERN);
public JspCompiler() {
}
// Generate the JavaScript code that then output the final text
public String compileJsp(String source, String className) throws FacesException {
StringBuilder javaSource = new StringBuilder(2048);
source = generateHeader(javaSource, source, className);
source = extractBody(source);
int slen = source.length();
for(int pos=0; pos<slen; ) {
int exp = source.indexOf(START_TAG,pos);
int end = exp>=0 ? exp : slen;
if(end>pos) {
javaSource.append(" emit(out,");
String emit = TextUtil.toJavaString(source.substring(pos,end),true);
javaSource.append(emit);
pos = end;
javaSource.append(");\n");
}
if(exp>=0) {
int stend = source.indexOf(END_TAG,exp+2);
if(stend<0) {
throw new FacesExceptionEx(null,"{0} without a closing {1}",START_TAG,END_TAG);
}
if(source.charAt(exp+2)=='=') {
String sub = source.substring(exp+3,stend).trim();
javaSource.append(" emit(out,");
String emit = sub;
javaSource.append(emit);
javaSource.append(");\n");
} else {
String sub = source.substring(exp+2,stend).trim();
javaSource.append(sub);
if(!sub.endsWith(";")) {
javaSource.append(";");
}
javaSource.append("\n");
}
pos = stend+2;
}
}
generateFooter(javaSource, className);
return javaSource.toString();
}
public static String cleanCode(String source) throws FacesException {
// remove the initial copyright comment
int start = StringUtil.indexOfIgnoreCase(source, "<!DOCTYPE");
if(start>=0) {
return source.substring(start).trim();
}
return source;
}
public static String extractBody(String source) throws FacesException {
int start = StringUtil.indexOfIgnoreCase(source, "<body>");
int end = StringUtil.indexOfIgnoreCase(source, "</body>");
if(start>=0 && end>=start+6) {
return source.substring(start+6,end).trim();
}
return source;
}
protected String generateHeader(StringBuilder b, String source, String className) {
int pos = className.lastIndexOf('.');
String genPackage = pos>=0 ? className.substring(0,pos) : null;
String genClass = pos>=0 ? className.substring(pos+1) :className;
if(StringUtil.isNotEmpty(genPackage)) {
b.append("package ").append(genPackage).append(";\n");
b.append("\n");
}
source = generateImports(b, source);
b.append("public class ").append(genClass).append(" extends nsf.playground.jsp.JspFragment {\n");
source = generateDeclarations(b, source);
b.append(" public void exec(JspWriter out, HttpServletRequest request, HttpServletResponse response) throws Exception {\n");
return source;
}
protected String generateImports(StringBuilder b, String source) throws FacesException {
// Std imports
b.append("import javax.servlet.http.HttpServletRequest;\n");
b.append("import javax.servlet.http.HttpServletResponse;\n");
b.append("import javax.servlet.jsp.JspWriter;\n");
// Then import from the JSP page
Matcher matcher = importPattern.matcher(source);
while(matcher.find()) {
String imp = matcher.group(1);
String[] allImp = StringUtil.splitString(imp, ',');
for(int i=0; i<allImp.length; i++) {
b.append("import ").append(allImp[i]).append(";\n");
}
}
b.append("\n");
return source;
}
protected String generateDeclarations(StringBuilder b, String source) throws FacesException {
while(true) {
int dec = source.indexOf(START_DECLARATION);
if(dec<0) {
break;
}
int stend = source.indexOf(END_DECLARATION,dec+2);
if(stend<0) {
throw new FacesExceptionEx(null,"{0} without a closing {1}",START_DECLARATION,END_DECLARATION);
}
String text = source.substring(dec+3,stend).trim();
b.append('\n');
b.append(text);
b.append('\n');
source = source.substring(0,dec)+source.substring(stend+END_DECLARATION.length(),source.length());
}
return source;
}
protected void generateFooter(StringBuilder b, String className) {
b.append(" } // exec\n");
b.append("} // class\n");
}
/*
public static void main(String[] args) {
// test("<body>Simple JSP</body>");
// test("<html><head><%@page import=\"mypack1\"%></head><body>Simple JSP</body></html>");
// test("<html><head><%@page import=\"mypack1\"%></head><body>Simple JSP<span><%=myfunc()%></span></body></html>");
// test("<html><head><%@page import=\"mypack1\"%></head><body><%Object o = factory;%>Simple JSP<span><%=myfunc()%></span></body></html>");
// test("<html><head><%@page import=\"mypack1\"%></head><body><%Object o = factory%>Simple JSP<span><%=myfunc()%></span></body></html>");
test("<html><head><%@page import=\"mypack1\"%><%@page import=\"mypack2\"%></head><body><%Object o = factory%>Simple JSP<span><%=myfunc()%></span></body></html>");
}
private static void test(String s) {
try {
System.out.println(StringUtil.format("======================================================="));
System.out.println(StringUtil.format("Pattern: {0}",IMPORTPATTERN));
System.out.println(StringUtil.format(">>>>Source:\n{0}\n",s));
JspCompiler p = new JspCompiler();
String javaCode = p.compileJsp(s,"myPackage.MyClass");
System.out.println(StringUtil.format(">>>>Generated Java class:\n{0}\n",javaCode));
} catch(Exception e) {
System.out.println(StringUtil.format("Error while processing text"));
e.printStackTrace();
}
}
*/
}