FreeMarker代码分析第九篇

2021SC@SDUSC

jsp包

FreemarkerTag.java

代码分析

package freemarker.ext.jsp;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTag;
import javax.servlet.jsp.tagext.Tag;

import freemarker.template.Configuration;
import freemarker.template.SimpleHash;
import freemarker.template.Template;

/**
 * 允许FreeMarker在JSP中使用模板的JSP标签
 */
@Deprecated
public class FreemarkerTag implements BodyTag {
    private Tag parent;
    private BodyContent bodyContent;
    private PageContext pageContext;
    private SimpleHash root;
    private Template template;
    private boolean caching = true;
    private String name = "";
    
    public boolean getCaching() {
        return caching;
    }

    public void setCaching(boolean caching) {
        this.caching = caching;
    }

    public void setName(String name) {
        this.name = name == null ? "" : name;
    }
    
    @Override
    public Tag getParent() {
        return parent;
    }

    @Override
    public void setParent(Tag parent) {
        this.parent = parent;
    }

    @Override
    public int doStartTag() {
        return EVAL_BODY_BUFFERED;
    }

    @Override
    public void setBodyContent(BodyContent bodyContent) {
        this.bodyContent = bodyContent;
    }

    @Override
    public void setPageContext(PageContext pageContext) {
        this.pageContext = pageContext;
        root = null;
    }

    @Override
    public void doInitBody() {
    }

    @Override
    public int doAfterBody() {
        return SKIP_BODY;
    }

    @Override
    public void release() {
        root = null;
        template = null;
        name = "";
    }

    @Override
    public int doEndTag()
        throws JspException {
        if (bodyContent == null)
            return EVAL_PAGE;

        try {
            if (template == null) {
                template = new Template(name, bodyContent.getReader());
            }

            if (root == null) {
                root = new SimpleHash();
                root.put("page", new JspContextModel(pageContext, JspContextModel.PAGE_SCOPE));
                root.put("request", new JspContextModel(pageContext, JspContextModel.REQUEST_SCOPE));
                root.put("session", new JspContextModel(pageContext, JspContextModel.SESSION_SCOPE));
                root.put("application", new JspContextModel(pageContext, JspContextModel.APPLICATION_SCOPE));
                root.put("any", new JspContextModel(pageContext, JspContextModel.ANY_SCOPE));
            }
            template.process(root, pageContext.getOut());
        } catch (Exception e) {
            try {
                pageContext.handlePageException(e);
            } catch (ServletException | IOException e2) {
                throw new JspException(e2.getMessage());
            }
        } finally {
            if (!caching) {
                template = null;
            }
        }

        return EVAL_PAGE;
    }
}

JspContextModel.java

代码分析

package freemarker.ext.jsp;

import javax.servlet.jsp.PageContext;

import freemarker.ext.beans.BeansWrapper;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;

/**
 * @Deprecated 该类被用于装饰 {@link FreemarkerTag}.
 */
@Deprecated
class JspContextModel
implements
    TemplateHashModel {
    public static final int ANY_SCOPE = -1;
    public static final int PAGE_SCOPE = PageContext.PAGE_SCOPE;
    public static final int REQUEST_SCOPE = PageContext.REQUEST_SCOPE;
    public static final int SESSION_SCOPE = PageContext.SESSION_SCOPE;
    public static final int APPLICATION_SCOPE = PageContext.APPLICATION_SCOPE;

    private final PageContext pageContext;
    private final int scope;

    public JspContextModel(PageContext pageContext, int scope) {
        this.pageContext = pageContext;
        this.scope = scope;
    }

    @Override
    public TemplateModel get(String key) throws TemplateModelException {
        Object bean = scope == ANY_SCOPE ? pageContext.findAttribute(key) : pageContext.getAttribute(key, scope);
        return BeansWrapper.getDefaultInstance().wrap(bean);
    }

    @Override
    public boolean isEmpty() {
        return false;
    }
}

JspTagModelBase.java

代码分析

package freemarker.ext.jsp;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import freemarker.core._DelayedJQuote;
import freemarker.core._DelayedShortClassName;
import freemarker.core._ErrorDescriptionBuilder;
import freemarker.core._TemplateModelException;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.jsp.SimpleTagDirectiveModel.TemplateExceptionWrapperJspException;
import freemarker.template.ObjectWrapper;
import freemarker.template.ObjectWrapperAndUnwrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.utility.StringUtil;

class JspTagModelBase {
    protected final String tagName;
    private final Class tagClass;
    private final Method dynaSetter;
    private final Map propertySetters = new HashMap();
    
    protected JspTagModelBase(String tagName, Class tagClass) throws IntrospectionException {
        this.tagName = tagName;
        this.tagClass = tagClass;
        BeanInfo bi = Introspector.getBeanInfo(tagClass);
        PropertyDescriptor[] pda = bi.getPropertyDescriptors();
        for (int i = 0; i < pda.length; i++) {
            PropertyDescriptor pd = pda[i];
            Method m = pd.getWriteMethod();
            if (m != null) {
                propertySetters.put(pd.getName(), m);
            }
        }
        // 判断这个标签(tag)是否实现了JSP2.0 的动态属性,接口,
        以便允许使用任意属性,方法
        Method dynaSetter;
        try {
            dynaSetter = tagClass.getMethod("setDynamicAttribute",
                            new Class[] {String.class, String.class, Object.class});
        } catch (NoSuchMethodException nsme) {
            dynaSetter = null;
        }
        this.dynaSetter = dynaSetter;
    }
    
    Object getTagInstance() throws IllegalAccessException, InstantiationException {
        return tagClass.newInstance();
    }
    
    void setupTag(Object tag, Map args, ObjectWrapper wrapper)
    throws TemplateModelException, 
        InvocationTargetException, 
        IllegalAccessException {
        if (args != null && !args.isEmpty()) {
            ObjectWrapperAndUnwrapper unwrapper = 
                    wrapper instanceof ObjectWrapperAndUnwrapper ? (ObjectWrapperAndUnwrapper) wrapper
                            : BeansWrapper.getDefaultInstance();  // [2.4] Throw exception in this case
            final Object[] argArray = new Object[1];
            for (Iterator iter = args.entrySet().iterator(); iter.hasNext(); ) {
                final Map.Entry entry = (Map.Entry) iter.next();
                final Object arg = unwrapper.unwrap((TemplateModel) entry.getValue());
                argArray[0] = arg;
                final Object paramName = entry.getKey();
                Method setterMethod = (Method) propertySetters.get(paramName);
                if (setterMethod == null) {
                    if (dynaSetter == null) {
                        throw new TemplateModelException("Unknown property "
                                + StringUtil.jQuote(paramName.toString())
                                + " on instance of " + tagClass.getName());
                    } else {
                        dynaSetter.invoke(tag, null, paramName, argArray[0]);
                    }
                } else {
                    if (arg instanceof BigDecimal) {
                        argArray[0] = BeansWrapper.coerceBigDecimal(
                                (BigDecimal) arg, setterMethod.getParameterTypes()[0]);
                    }
                    try {
                        setterMethod.invoke(tag, argArray);
                    } catch (Exception e) {
                        final Class setterType = setterMethod.getParameterTypes()[0];
                        final _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
                                "Failed to set JSP tag parameter ", new _DelayedJQuote(paramName),
                                " (declared type: ", new _DelayedShortClassName(setterType)
                                + ", actual value's type: ",
                                (argArray[0] != null
                                        ? new _DelayedShortClassName(argArray[0].getClass()) : "Null"),
                                "). See cause exception for the more specific cause...");
                        if (e instanceof IllegalArgumentException && !(setterType.isAssignableFrom(String.class))
                                && argArray[0] != null && argArray[0] instanceof String) {
                            desc.tip("This problem is often caused by unnecessary parameter quotation. Paramters "
                                    + "aren't quoted in FTL, similarly as they aren't quoted in most languages. "
                                    + "For example, these parameter assignments are wrong: ",
                                    "<@my.tag p1=\"true\" p2=\"10\" p3=\"${someVariable}\" p4=\"${x+1}\" />",
                                    ". The correct form is: ",
                                    "<@my.tag p1=true p2=10 p3=someVariable p4=x+1 />",
                                    ". Only string literals are quoted (regardless of where they occur): ",
                                    "<@my.box style=\"info\" message=\"Hello ${name}!\" width=200 />",
                                    ".");
                        }
                        throw new _TemplateModelException(e, null, desc);
                    }
                }
            }
        }
    }

    protected final TemplateModelException toTemplateModelExceptionOrRethrow(Exception e) throws TemplateModelException {
        if (e instanceof RuntimeException && !isCommonRuntimeException((RuntimeException) e)) {
            throw (RuntimeException) e;
        }
        if (e instanceof TemplateModelException) {
            throw (TemplateModelException) e;
        }
        if (e instanceof TemplateExceptionWrapperJspException) {
            return toTemplateModelExceptionOrRethrow(((TemplateExceptionWrapperJspException) e).getCause());
        }
        return new TemplateModelException(
                "Error while invoking the " + StringUtil.jQuote(tagName) + " JSP custom tag; see cause exception",
                e instanceof TemplateException, e);
    }

    /**
     * Runtime exceptions that we don't want to propagate, instead we warp them into a more helpful exception. These are
     * the ones where it's very unlikely that someone tries to catch specifically these around
     * {@link Template#process(Object, java.io.Writer)}.
     */
    private boolean isCommonRuntimeException(RuntimeException e) {
        final Class eClass = e.getClass();
        // We deliberately don't accept sub-classes. Those are possibly application specific and some want to catch them
        // outside the template.
        return eClass == NullPointerException.class
                || eClass == IllegalArgumentException.class
                || eClass == ClassCastException.class
                || eClass == IndexOutOfBoundsException.class;
    }
    
}

JspWriterAdapter.java

代码分析

package freemarker.ext.jsp;

import java.io.IOException;
import java.io.Writer;

import javax.servlet.jsp.JspWriter;

import freemarker.template.utility.SecurityUtilities;

class JspWriterAdapter extends JspWriter {
    static final char[] NEWLINE = SecurityUtilities.getSystemProperty("line.separator", "\n").toCharArray();
    
    private final Writer out;
    
    JspWriterAdapter(Writer out) {
        super(0, true);
        this.out = out;
    }
    
    @Override
    public String toString() {
        return "JspWriterAdapter wrapping a " + out.toString();
    }
    
    @Override
    public void clear() throws IOException {
        throw new IOException("Can't clear");
    }

    @Override
    public void clearBuffer() throws IOException {
        throw new IOException("Can't clear");
    }

    @Override
    public void close() throws IOException {
        throw new IOException("Close not permitted.");
    }

    @Override
    public void flush() throws IOException {
        out.flush();
    }

    @Override
    public int getRemaining() {
        return 0;
    }

    @Override
    public void newLine() throws IOException {
        out.write(NEWLINE);
    }

    @Override
    public void print(boolean arg0) throws IOException {
        out.write(arg0 ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
    }

    @Override
    public void print(char arg0) throws IOException {
        out.write(arg0);
    }

    @Override
    public void print(char[] arg0) throws IOException {
        out.write(arg0);
    }

    @Override
    public void print(double arg0) throws IOException {
        out.write(Double.toString(arg0));
    }

    @Override
    public void print(float arg0) throws IOException {
        out.write(Float.toString(arg0));
    }

    @Override
    public void print(int arg0) throws IOException {
        out.write(Integer.toString(arg0));
    }

    @Override
    public void print(long arg0) throws IOException {
        out.write(Long.toString(arg0));
    }

    @Override
    public void print(Object arg0) throws IOException {
        out.write(arg0 == null ? "null" : arg0.toString());
    }

    @Override
    public void print(String arg0) throws IOException {
        out.write(arg0);
    }

    @Override
    public void println() throws IOException {
        newLine();
    }

    @Override
    public void println(boolean arg0) throws IOException {
        print(arg0);
        newLine();
    }

    @Override
    public void println(char arg0) throws IOException {
        print(arg0);
        newLine();
    }

    @Override
    public void println(char[] arg0) throws IOException {
        print(arg0);
        newLine();
    }

    @Override
    public void println(double arg0) throws IOException {
        print(arg0);
        newLine();
    }

    @Override
    public void println(float arg0) throws IOException {
        print(arg0);
        newLine();
    }

    @Override
    public void println(int arg0) throws IOException {
        print(arg0);
        newLine();
    }

    @Override
    public void println(long arg0) throws IOException {
        print(arg0);
        newLine();
    }

    @Override
    public void println(Object arg0) throws IOException {
        print(arg0);
        newLine();
    }

    @Override
    public void println(String arg0) throws IOException {
        print(arg0);
        newLine();
    }

    @Override
    public void write(int c) throws IOException {
        out.write(c);
    }
    
    @Override
    public void write(char[] arg0, int arg1, int arg2)
        throws IOException {
        out.write(arg0, arg1, arg2);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值