/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 freemarker.ext.jsp;
import java.io.
IOException;
import java.io.
PrintWriter;
import java.io.
Writer;
import java.util.
ArrayList;
import java.util.
Collections;
import java.util.
Enumeration;
import java.util.
List;
import java.util.
ListIterator;
import javax.servlet.
GenericServlet;
import javax.servlet.
Servlet;
import javax.servlet.
ServletConfig;
import javax.servlet.
ServletContext;
import javax.servlet.
ServletException;
import javax.servlet.
ServletOutputStream;
import javax.servlet.
ServletRequest;
import javax.servlet.
ServletResponse;
import javax.servlet.http.
HttpServletRequest;
import javax.servlet.http.
HttpServletResponse;
import javax.servlet.http.
HttpServletResponseWrapper;
import javax.servlet.http.
HttpSession;
import javax.servlet.jsp.
JspWriter;
import javax.servlet.jsp.
PageContext;
import javax.servlet.jsp.tagext.
BodyContent;
import freemarker.core.
Environment;
import freemarker.ext.servlet.
FreemarkerServlet;
import freemarker.ext.servlet.
HttpRequestHashModel;
import freemarker.ext.servlet.
ServletContextHashModel;
import freemarker.ext.util.
WrapperTemplateModel;
import freemarker.template.
AdapterTemplateModel;
import freemarker.template.
ObjectWrapper;
import freemarker.template.
ObjectWrapperAndUnwrapper;
import freemarker.template.
TemplateBooleanModel;
import freemarker.template.
TemplateDateModel;
import freemarker.template.
TemplateHashModelEx;
import freemarker.template.
TemplateModel;
import freemarker.template.
TemplateModelException;
import freemarker.template.
TemplateModelIterator;
import freemarker.template.
TemplateNumberModel;
import freemarker.template.
TemplateScalarModel;
import freemarker.template.
_TemplateAPI;
import freemarker.template.utility.
UndeclaredThrowableException;
/**
*/
abstract class
FreeMarkerPageContext extends
PageContext implements
TemplateModel {
private static final
Class OBJECT_CLASS =
Object.class;
private final
Environment environment;
private final int
incompatibleImprovements;
private
List tags = new
ArrayList();
private
List outs = new
ArrayList();
private final
GenericServlet servlet;
private
HttpSession session;
private final
HttpServletRequest request;
private final
HttpServletResponse response;
private final
ObjectWrapper wrapper;
private final
ObjectWrapperAndUnwrapper unwrapper;
private
JspWriter jspOut;
protected
FreeMarkerPageContext() throws
TemplateModelException {
environment =
Environment.
getCurrentEnvironment();
incompatibleImprovements =
environment.
getConfiguration().
getIncompatibleImprovements().
intValue();
TemplateModel appModel =
environment.
getGlobalVariable(
FreemarkerServlet.
KEY_APPLICATION_PRIVATE);
if (!(
appModel instanceof
ServletContextHashModel)) {
appModel =
environment.
getGlobalVariable(
FreemarkerServlet.
KEY_APPLICATION);
}
if (
appModel instanceof
ServletContextHashModel) {
this.
servlet = ((
ServletContextHashModel)
appModel).
getServlet();
} else {
throw new
TemplateModelException("Could not find an instance of " +
ServletContextHashModel.class.
getName() +
" in the data model under either the name " +
FreemarkerServlet.
KEY_APPLICATION_PRIVATE + " or " +
FreemarkerServlet.
KEY_APPLICATION);
}
TemplateModel requestModel =
environment.
getGlobalVariable(
FreemarkerServlet.
KEY_REQUEST_PRIVATE);
if (!(
requestModel instanceof
HttpRequestHashModel)) {
requestModel =
environment.
getGlobalVariable(
FreemarkerServlet.
KEY_REQUEST);
}
if (
requestModel instanceof
HttpRequestHashModel) {
HttpRequestHashModel reqHash = (
HttpRequestHashModel)
requestModel;
request =
reqHash.
getRequest();
session =
request.
getSession(false);
response =
reqHash.
getResponse();
wrapper =
reqHash.
getObjectWrapper();
unwrapper = this.
wrapper instanceof
ObjectWrapperAndUnwrapper
? (
ObjectWrapperAndUnwrapper) this.
wrapper : null;
} else {
throw new
TemplateModelException("Could not find an instance of " +
HttpRequestHashModel.class.
getName() +
" in the data model under either the name " +
FreemarkerServlet.
KEY_REQUEST_PRIVATE + " or " +
FreemarkerServlet.
KEY_REQUEST);
}
// Register page attributes as per spec
setAttribute(
REQUEST,
request);
setAttribute(
RESPONSE,
response);
if (
session != null)
setAttribute(
SESSION,
session);
setAttribute(
PAGE,
servlet);
setAttribute(
CONFIG,
servlet.
getServletConfig());
setAttribute(
PAGECONTEXT, this);
setAttribute(
APPLICATION,
servlet.
getServletContext());
}
ObjectWrapper getObjectWrapper() {
return
wrapper;
}
@
Override
public void initialize(
Servlet servlet,
ServletRequest request,
ServletResponse response,
String errorPageURL, boolean
needsSession, int
bufferSize,
boolean
autoFlush) {
throw new
UnsupportedOperationException();
}
@
Override
public void
release() {
}
@
Override
public void
setAttribute(
String name,
Object value) {
setAttribute(
name,
value,
PAGE_SCOPE);
}
@
Override
public void
setAttribute(
String name,
Object value, int
scope) {
switch(
scope) {
case
PAGE_SCOPE: {
try {
environment.
setGlobalVariable(
name,
wrapper.
wrap(
value));
break;
} catch (
TemplateModelException e) {
throw new
UndeclaredThrowableException(
e);
}
}
case
REQUEST_SCOPE: {
getRequest().
setAttribute(
name,
value);
break;
}
case
SESSION_SCOPE: {
getSession(true).
setAttribute(
name,
value);
break;
}
case
APPLICATION_SCOPE: {
getServletContext().
setAttribute(
name,
value);
break;
}
default: {
throw new
IllegalArgumentException("Invalid scope " +
scope);
}
}
}
@
Override
public
Object getAttribute(
String name) {
return
getAttribute(
name,
PAGE_SCOPE);
}
@
Override
public
Object getAttribute(
String name, int
scope) {
switch (
scope) {
case
PAGE_SCOPE: {
try {
final
TemplateModel tm =
environment.
getGlobalNamespace().
get(
name);
if (
incompatibleImprovements >=
_TemplateAPI.
VERSION_INT_2_3_22 &&
unwrapper != null) {
return
unwrapper.
unwrap(
tm);
} else { // Legacy behavior branch
if (
tm instanceof
AdapterTemplateModel) {
return ((
AdapterTemplateModel)
tm).
getAdaptedObject(
OBJECT_CLASS);
}
if (
tm instanceof
WrapperTemplateModel) {
return ((
WrapperTemplateModel)
tm).
getWrappedObject();
}
if (
tm instanceof
TemplateScalarModel) {
return ((
TemplateScalarModel)
tm).
getAsString();
}
if (
tm instanceof
TemplateNumberModel) {
return ((
TemplateNumberModel)
tm).
getAsNumber();
}
if (
tm instanceof
TemplateBooleanModel) {
return
Boolean.
valueOf(((
TemplateBooleanModel)
tm).
getAsBoolean());
}
if (
incompatibleImprovements >=
_TemplateAPI.
VERSION_INT_2_3_22
&&
tm instanceof
TemplateDateModel) {
return ((
TemplateDateModel)
tm).
getAsDate();
}
return
tm;
}
} catch (
TemplateModelException e) {
throw new
UndeclaredThrowableException("Failed to unwrapp FTL global variable",
e);
}
}
case
REQUEST_SCOPE: {
return
getRequest().
getAttribute(
name);
}
case
SESSION_SCOPE: {
HttpSession session =
getSession(false);
if (
session == null) {
return null;
}
return
session.
getAttribute(
name);
}
case
APPLICATION_SCOPE: {
return
getServletContext().
getAttribute(
name);
}
default: {
throw new
IllegalArgumentException("Invalid scope " +
scope);
}
}
}
@
Override
public
Object findAttribute(
String name) {
Object retval =
getAttribute(
name,
PAGE_SCOPE);
if (
retval != null) return
retval;
retval =
getAttribute(
name,
REQUEST_SCOPE);
if (
retval != null) return
retval;
retval =
getAttribute(
name,
SESSION_SCOPE);
if (
retval != null) return
retval;
return
getAttribute(
name,
APPLICATION_SCOPE);
}
@
Override
public void
removeAttribute(
String name) {
removeAttribute(
name,
PAGE_SCOPE);
removeAttribute(
name,
REQUEST_SCOPE);
removeAttribute(
name,
SESSION_SCOPE);
removeAttribute(
name,
APPLICATION_SCOPE);
}
@
Override
public void
removeAttribute(
String name, int
scope) {
switch(
scope) {
case
PAGE_SCOPE: {
environment.
getGlobalNamespace().
remove(
name);
break;
}
case
REQUEST_SCOPE: {
getRequest().
removeAttribute(
name);
break;
}
case
SESSION_SCOPE: {
HttpSession session =
getSession(false);
if (
session != null) {
session.
removeAttribute(
name);
}
break;
}
case
APPLICATION_SCOPE: {
getServletContext().
removeAttribute(
name);
break;
}
default: {
throw new
IllegalArgumentException("Invalid scope: " +
scope);
}
}
}
@
Override
public int
getAttributesScope(
String name) {
if (
getAttribute(
name,
PAGE_SCOPE) != null) return
PAGE_SCOPE;
if (
getAttribute(
name,
REQUEST_SCOPE) != null) return
REQUEST_SCOPE;
if (
getAttribute(
name,
SESSION_SCOPE) != null) return
SESSION_SCOPE;
if (
getAttribute(
name,
APPLICATION_SCOPE) != null) return
APPLICATION_SCOPE;
return 0;
}
@
Override
public
Enumeration getAttributeNamesInScope(int
scope) {
switch(
scope) {
case
PAGE_SCOPE: {
try {
return
new
TemplateHashModelExEnumeration(
environment.
getGlobalNamespace());
} catch (
TemplateModelException e) {
throw new
UndeclaredThrowableException(
e);
}
}
case
REQUEST_SCOPE: {
return
getRequest().
getAttributeNames();
}
case
SESSION_SCOPE: {
HttpSession session =
getSession(false);
if (
session != null) {
return
session.
getAttributeNames();
}
return
Collections.
enumeration(
Collections.
EMPTY_SET);
}
case
APPLICATION_SCOPE: {
return
getServletContext().
getAttributeNames();
}
default: {
throw new
IllegalArgumentException("Invalid scope " +
scope);
}
}
}
@
Override
public
JspWriter getOut() {
return
jspOut;
}
private
HttpSession getSession(boolean
create) {
if (
session == null) {
session =
request.
getSession(
create);
if (
session != null) {
setAttribute(
SESSION,
session);
}
}
return
session;
}
@
Override
public
HttpSession getSession() {
return
getSession(false);
}
@
Override
public
Object getPage() {
return
servlet;
}
@
Override
public
ServletRequest getRequest() {
return
request;
}
@
Override
public
ServletResponse getResponse() {
return
response;
}
@
Override
public
Exception getException() {
throw new
UnsupportedOperationException();
}
@
Override
public
ServletConfig getServletConfig() {
return
servlet.
getServletConfig();
}
@
Override
public
ServletContext getServletContext() {
return
servlet.
getServletContext();
}
@
Override
public void forward(
String url) throws
ServletException,
IOException {
//TODO: make sure this is 100% correct by looking at Jasper output
request.
getRequestDispatcher(
url).
forward(
request,
response);
}
@
Override
public void include(
String url) throws
ServletException,
IOException {
jspOut.
flush();
request.
getRequestDispatcher(
url).
include(
request,
response);
}
@
Override
public void include(
String url, boolean
flush) throws
ServletException,
IOException {
if (
flush) {
jspOut.
flush();
}
final
PrintWriter pw = new
PrintWriter(
jspOut);
request.
getRequestDispatcher(
url).
include(
request, new
HttpServletResponseWrapper(
response) {
@
Override
public
PrintWriter getWriter() {
return
pw;
}
@
Override
public
ServletOutputStream getOutputStream() {
throw new
UnsupportedOperationException("JSP-included resource must use getWriter()");
}
});
pw.
flush();
}
@
Override
public void
handlePageException(
Exception e) {
throw new
UnsupportedOperationException();
}
@
Override
public void
handlePageException(
Throwable e) {
throw new
UnsupportedOperationException();
}
@
Override
public
BodyContent pushBody() {
return (
BodyContent)
pushWriter(new
TagTransformModel.
BodyContentImpl(
getOut(), true));
}
@
Override
public
JspWriter pushBody(
Writer w) {
return
pushWriter(new
JspWriterAdapter(
w));
}
@
Override
public
JspWriter popBody() {
popWriter();
return (
JspWriter)
getAttribute(
OUT);
}
Object peekTopTag(
Class tagClass) {
for (
ListIterator iter =
tags.
listIterator(
tags.
size());
iter.
hasPrevious(); ) {
Object tag =
iter.
previous();
if (
tagClass.
isInstance(
tag)) {
return
tag;
}
}
return null;
}
void
popTopTag() {
tags.
remove(
tags.
size() - 1);
}
void
popWriter() {
jspOut = (
JspWriter)
outs.
remove(
outs.
size() - 1);
setAttribute(
OUT,
jspOut);
}
void
pushTopTag(
Object tag) {
tags.
add(
tag);
}
JspWriter pushWriter(
JspWriter out) {
outs.
add(
jspOut);
jspOut =
out;
setAttribute(
OUT,
jspOut);
return
out;
}
private static class
TemplateHashModelExEnumeration implements
Enumeration {
private final
TemplateModelIterator it;
private
TemplateHashModelExEnumeration(
TemplateHashModelEx hashEx) throws
TemplateModelException {
it =
hashEx.
keys().
iterator();
}
public boolean
hasMoreElements() {
try {
return
it.
hasNext();
} catch (
TemplateModelException tme) {
throw new
UndeclaredThrowableException(
tme);
}
}
public
Object nextElement() {
try {
return ((
TemplateScalarModel)
it.
next()).
getAsString();
} catch (
TemplateModelException tme) {
throw new
UndeclaredThrowableException(
tme);
}
}
}
}