package com.hundsun.jresplus.web.contain;
import java.io.IOException;
import java.io.Writer;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.context.Context;
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.Renderable;
import com.hundsun.jresplus.web.velocity.eventhandler.DirectOutput;
/**
* contain可以使用toolbox配置,也可使用containFilter配置在spring中
*
* @author LeoHu copy by sagahl copy by fish
*
*/
public class Contain {
private static final Log log = LogFactory.getLog(Contain.class);
/**
* 保存在request中,contain被调用的标记key,值为当前cotain被调用的深度
*/
public static final String ContainCounterKey = "_iContain";
private HttpServletRequest request;
private HttpServletResponse response;
private static final int recursiveLevel = 20;
private static final ThreadLocal<VaryInt> count = new ThreadLocal<VaryInt>() {
@Override
protected VaryInt initialValue() {
return new VaryInt();
}
};
public Contain() {
super();
}
public Contain(HttpServletRequest request, HttpServletResponse response) {
super();
this.request = request;
this.response = response;
}
public ControlRender setTemplate(String controlName) {
if (controlName == null) {
throw new NullPointerException(
"control template name can't be null.");
}
return new ControlRender(controlName);
}
public ControlRender get(String controlName) {
return setTemplate(controlName);
}
public class ControlRender implements DirectOutput, Renderable {
private String controlName;
private Set<ContainParameter> parameters = new HashSet<ContainParameter>();
public ControlRender(String controlName) {
this.controlName = controlName;
}
public ControlRender put(String key, Object value) {
parameters.add(new ContainParameter(Contain.this.request, key,
value));
return this;
}
public ControlRender setParameter(String key, Object value) {
return this.put(key, value);
}
public boolean render(InternalContextAdapter adapter, Writer writer)
throws IOException, MethodInvocationException,
ParseErrorException, ResourceNotFoundException {
if (enter()) {
log.error("contain recursive invoked,so exist.");
return true;
}
parameters.add(new ContainParameter(Contain.this.request,
ContainCounterKey, count.get().i));
ResponseContainWrapper wrapper = new ResponseContainWrapper(
Contain.this.response, writer);
try {
Contain.this.request.getRequestDispatcher(controlName).include(
Contain.this.request, wrapper);
if (this.parameters != null) {
for (ContainParameter cp : this.parameters) {
cp.recover(Contain.this.request);
}
}
} catch (ServletException e) {
if (log.isErrorEnabled()) {
log.error("error in control render.", e);
}
writer.write(e.getMessage());
} catch (IOException e) {
if (log.isErrorEnabled()) {
log.error("error in control render.", e);
}
writer.write(e.getMessage());
} finally {
leave();
}
return true;
}
@Override
public String toString() {
if (enter()) {
log.error("contain recursive invoked,so exist.");
return "";
}
parameters.add(new ContainParameter(Contain.this.request,
ContainCounterKey, count.get().i));
ResponseStringWriterWrapper responseWrapper = new ResponseStringWriterWrapper(
Contain.this.response);
try {
Contain.this.request.getRequestDispatcher(controlName).include(
Contain.this.request, responseWrapper);
String back = responseWrapper.getStringWriter().toString();
if (this.parameters != null) {
for (ContainParameter cp : this.parameters) {
cp.recover(Contain.this.request);
}
}
return back;
} catch (ServletException e) {
if (log.isErrorEnabled()) {
log.error("error in control render.", e);
}
return e.getMessage();
} catch (IOException e) {
if (log.isErrorEnabled()) {
log.error("error in control render.", e);
}
return e.getMessage();
} finally {
leave();
}
}
/**
* 调用了一次,返回是否超过递归层次
*
* @return
*/
private boolean enter() {
VaryInt vi = count.get();
int now = vi.add();
if (now >= recursiveLevel) {
vi.i = 0;
return true;
}
return false;
}
private void leave() {
VaryInt vi = count.get();
vi.sub();
}
}
private static class ContainParameter {
private String key;
private Object original;
public ContainParameter(HttpServletRequest request, String key,
Object newValue) {
this.key = key;
this.original = request.getAttribute(key);
request.setAttribute(key, newValue);
}
public void recover(HttpServletRequest request) {
request.setAttribute(this.key, this.original);
}
}
private static final class VaryInt {
int i = 0;
public int add() {
i++;
return i;
}
public int sub() {
i--;
return i;
}
}
public static boolean isInContain(HttpServletRequest request) {
// 如果是在执行一个contain渲染,则无需layout,执行普通渲染逻辑即可
Integer cCount = (Integer) request
.getAttribute(Contain.ContainCounterKey);
if (cCount != null && cCount > 0) {
return true;
}
return false;
}
public static boolean isInContain(Context context) {
// 如果是在执行一个contain渲染,则无需layout,执行普通渲染逻辑即可
Integer cCount = (Integer) context.get(Contain.ContainCounterKey);
if (cCount != null && cCount > 0) {
return true;
}
return false;
}
}