📄 parser.java
字号:
/* Parser.java{{IS_NOTE Purpose: Description: History: Sat Sep 17 12:12:41 2005, Created by tomyeh}}IS_NOTECopyright (C) 2004 Potix Corporation. All Rights Reserved.{{IS_RIGHT This program is distributed under GPL Version 2.0 in the hope that it will be useful, but WITHOUT ANY WARRANTY.}}IS_RIGHT*/package org.zkoss.web.servlet.dsp.impl;import java.util.Map;import java.util.HashMap;import java.util.Iterator;import java.io.IOException;import java.io.FileNotFoundException;import java.net.URL;import javax.servlet.jsp.el.FunctionMapper;import org.zkoss.lang.D;import org.zkoss.lang.Classes;import org.zkoss.util.logging.Log;import org.zkoss.util.resource.Locator;import org.zkoss.el.SimpleMapper;import org.zkoss.idom.input.SAXBuilder;import org.zkoss.idom.Element;import org.zkoss.idom.util.IDOMs;import org.zkoss.web.mesg.MWeb;import org.zkoss.web.servlet.ServletException;import org.zkoss.web.servlet.dsp.*;import org.zkoss.web.servlet.dsp.action.Page;import org.zkoss.web.servlet.dsp.action.Action;/** * Used to parse a DSP page into a meta format called * {@link Interpretation}. * * @author tomyeh */public class Parser { private static final Log log = Log.lookup(Parser.class); /** Parses the content into a meta format * * @param content the content to parse; never null. * @param ctype the content type. Optional. It is used only if * no page action at all. If it is not specified and not page * action, "text/html" is assumed. * @param fm a pre-defined function mapper, or null to ignore. * @param loc used to locate the resource such as taglib. * It could null only if DSP contains no such resource. */ public Interpretation parse(String content, String ctype, FunctionMapper fm, Locator loc) throws javax.servlet.ServletException, IOException { final Context ctx = new Context(content, fm, loc); final RootNode root = new RootNode(ctx.mapper); parse0(ctx, root, 0, content.length()); if (!ctx.pageDefined) { //We always create a page definition if (D.ON && log.debugable()) log.debug("Use default content type: "+ctype); final ActionNode action = new ActionNode(Page.class, 0); root.addChild(0, action); final Map attrs = new HashMap(2); attrs.put("contentType", ctype != null ? ctype: "text/html"); applyAttrs("page", action, attrs); } return root; } /** Recursively parse the content into a tree of {@link Node}. */ private static void parse0(Context ctx, Node parent, int from, int to) throws javax.servlet.ServletException, IOException { boolean esc = false; final StringBuffer sb = new StringBuffer(512); for (int j = from; j < to; ++j) { char cc = ctx.content.charAt(j); //We only recognize <%, <\%, ${, $\{ and <xx:yy> switch (cc) { case '<': if (j + 1 < to) { char c2 = ctx.content.charAt(j + 1); if (c2 == '\\') { if (j + 2 < to && ctx.content.charAt(j + 2) == '%') ++j; //skip '\\' } else if (c2 == '%') { addText(parent, sb); j = parseControl(ctx, parent, j, to); continue; } else { final int oldLines = ctx.nLines; int k = skipWhitespaces(ctx, j + 1, to); int l = nextSeparator(ctx, k, to); if (l >= to || l == k || ctx.content.charAt(l) != ':') { ctx.nLines = oldLines; break; //bypass what we don't recognize } final String prefix = ctx.content.substring(k, l); if (!ctx.hasPrefix(prefix)) { ctx.nLines = oldLines; break; //bypass what we don't recognize } addText(parent, sb); j = parseAction(ctx, parent, prefix, l, to); continue; } } break; case '$': if (j + 1 < to) { char c2 = ctx.content.charAt(j + 1); if (c2 == '\\') { if (j + 2 < to && ctx.content.charAt(j + 2) == '{') ++j; //skip '\\' } else if (c2 == '{') { addText(parent, sb); j = parseEL(ctx, parent, j, to); continue; } } break; case '\n': ++ctx.nLines; } sb.append(cc); } addText(parent, sb); } /** Parses a control (e.g., <% page %>) starting at from, * and returns the postion of '>' (in %>). */ private static int parseControl(Context ctx, Node parent, int from, int to) throws javax.servlet.ServletException, IOException { int j = from + 2; if (j + 1 >= to) throw new ServletException(MWeb.DSP_ACTION_NOT_TERMINATED, new Object[] {null, new Integer(ctx.nLines)}); //0. comment char cc = ctx.content.charAt(j); if (cc == '-' && ctx.content.charAt(j + 1) == '-') { //comment for (int end = to - 4;; ++j) { if (j > end) throw new ServletException(MWeb.DSP_COMMENT_NOT_TERMINATED, new Integer(ctx.nLines)); if (ctx.content.charAt(j) == '\n') ++ctx.nLines; else if (startsWith(ctx.content, j, to, "--%>")) return j + 3; } } if (cc != '@') throw new ServletException(MWeb.DSP_EXPECT_CHARACTER, new Object[] {new Character('@'), new Integer(ctx.nLines)}); //1: which control j = skipWhitespaces(ctx, j + 1, to); int k = nextSeparator(ctx, j, to); if (k >= to) throw new ServletException(MWeb.DSP_ACTION_NOT_TERMINATED, new Object[] {null, new Integer(ctx.nLines)}); final ActionNode action; final String ctlnm = ctx.content.substring(j, k); if ("taglib".equals(ctlnm)) { action = null; } else if ("page".equals(ctlnm)) { ctx.pageDefined = true; parent.addChild(action = new ActionNode(Page.class, ctx.nLines)); } else { throw new ServletException(MWeb.DSP_UNKNOWN_ACTION, new Object[] {ctlnm, new Integer(ctx.nLines)}); } //2: parse attributes final Map attrs = new HashMap(); k = parseAttrs(ctx, attrs, ctlnm, k, to); cc = ctx.content.charAt(k); if (cc != '%') throw new ServletException(MWeb.DSP_EXPECT_CHARACTER, new Object[] {new Character('%'), new Integer(ctx.nLines)}); if (action == null) { //taglib final String uri = (String)attrs.get("uri"), prefix = (String)attrs.get("prefix"); if (prefix == null || uri == null) throw new ServletException(MWeb.DSP_TAGLIB_ATTRIBUTE_REQUIRED, new Integer(ctx.nLines)); ctx.loadTaglib(prefix, uri); } else { applyAttrs(ctlnm, action, attrs); } if (++k >= to || ctx.content.charAt(k) != '>') throw new ServletException(MWeb.DSP_ACTION_NOT_TERMINATED, new Object[] {ctlnm, new Integer(ctx.nLines)}); return k; } /** Parses an action (e.g., <c:forEach...>...</c:forEach>). * @param from the position of ':' * @return the postion of the last '>'. */ private static int parseAction(Context ctx, Node parent, String prefix, int from, int to) throws javax.servlet.ServletException, IOException { //1: which action int j = skipWhitespaces(ctx, from + 1, to); int k = nextSeparator(ctx, j, to); if (k >= to) throw new ServletException(MWeb.DSP_ACTION_NOT_TERMINATED, new Object[] {prefix+':', new Integer(ctx.nLines)}); if (k == j) throw new ServletException(MWeb.DSP_ACTION_REQUIRED, new Integer(ctx.nLines)); final String actnm = ctx.content.substring(j, k); final Class actcls = ctx.getActionClass(prefix, actnm); if (actcls == null) throw new ServletException(MWeb.DSP_UNKNOWN_ACTION, new Object[] {prefix+':'+actnm, new Integer(ctx.nLines)}); final ActionNode action = new ActionNode(actcls, ctx.nLines); parent.addChild(action); if (D.ON && log.debugable()) log.debug("Action "+actnm); //2: action's attributes final Map attrs = new HashMap(); j = parseAttrs(ctx, attrs, actnm, k, to); char cc = ctx.content.charAt(j); boolean ended = cc == '/'; if (!ended && cc != '>') throw new ServletException(MWeb.DSP_UNEXPECT_CHARACTER, new Object[] {new Character(cc), new Integer(ctx.nLines)}); applyAttrs(actnm, action, attrs); if (ended) { if (j + 1 >= to || ctx.content.charAt(j + 1) != '>') throw new ServletException(MWeb.DSP_ACTION_NOT_TERMINATED, new Object[] {prefix+':'+actnm, new Integer(action.getLineNumber())}); return j + 1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -