All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sunlabs.brazil.template.TemplateRunner Maven / Gradle / Ivy

The newest version!
/*
 * TemplateRunner.java
 *
 * Brazil project web application toolkit,
 * export version: 2.3 
 * Copyright (c) 1999-2006 Sun Microsystems, Inc.
 *
 * Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License Version 
 * 1.0 (the "License"). You may not use this file except in compliance with 
 * the License. A copy of the License is included as the file "license.terms",
 * and also available at http://www.sun.com/
 * 
 * The Original Code is from:
 *    Brazil project web application toolkit release 2.3.
 * The Initial Developer of the Original Code is: suhler.
 * Portions created by suhler are Copyright (C) Sun Microsystems, Inc.
 * All Rights Reserved.
 * 
 * Contributor(s): cstevens, drach, suhler.
 *
 * Version:  2.4
 * Created by suhler on 99/05/06
 * Last modified by suhler on 06/08/01 14:07:44
 *
 * Version Histories:
 *
 * 2.4 06/08/01-14:07:44 (suhler)
 *   added templateFromTag() to allow greater introspection of our template classes
 *   minor doc fixes
 *   .
 *
 * 2.3 03/08/01-16:19:00 (suhler)
 *   fixes for javadoc
 *
 * 2.2 03/07/09-12:27:29 (suhler)
 *   added tagPrefix methods
 *
 * 2.1 02/10/01-16:36:48 (suhler)
 *   version change
 *
 * 1.34 02/07/24-10:47:07 (suhler)
 *   doc updates
 *
 * 1.33 02/07/19-15:10:01 (suhler)
 *   added (experimental) tagPrefix option to handle XML namespaces
 *
 * 1.32 02/06/24-15:39:47 (suhler)
 *   Better diagnostics for templates without a public constructor
 *
 * 1.31 02/05/01-11:28:06 (suhler)
 *   fix sccs version info
 *
 * 1.30 01/10/03-13:54:03 (suhler)
 *   added "tags seen" for diagnostic purposes
 *
 * 1.29 01/08/03-18:13:04 (suhler)
 *   remove white space from class names
 *
 * 1.28 01/05/11-14:52:22 (suhler)
 *   Make sure templates are Assignable from "TemplateInterface" instead
 *   of "Template", so templates don't have to extend Template
 *
 * 1.27 01/05/02-15:33:49 (drach)
 *   Add template tokens.
 *
 * 1.26 01/04/09-18:36:24 (suhler)
 *   fixed bug in
 *   _xx
 *
 * 1.25 00/12/11-13:30:24 (suhler)
 *   doc typo
 *
 * 1.24 00/11/21-13:24:21 (suhler)
 *   better error messages
 *
 * 1.23 00/10/31-10:19:33 (suhler)
 *   doc fixes
 *
 * 1.22 00/10/20-15:43:17 (suhler)
 *   Removed getContent() due to race condition.  process() now returns
 *   the content.
 *
 * 1.21 00/10/16-13:34:36 (suhler)
 *   Added 2 more methods:
 *   "string" dispatches on all text between tags
 *   "defaultTag" dispatches on all tags that aren't otherwised matched
 *   .
 *
 * 1.20 00/07/06-15:59:26 (suhler)
 *   doc fixes
 *
 * 1.19 00/07/05-14:11:19 (cstevens)
 *   Server object is in RewriteContext used by templates, so templates (such as
 *   the TclServerTemplate) can be initialized with the server and prefix, similar
 *   to how Handlers are initialized.
 *
 * 1.18 00/05/31-13:50:18 (suhler)
 *   name change, docs
 *
 * 1.17 00/04/12-15:55:34 (cstevens)
 *   Work with SerialPersist SessionManager
 *
 * 1.16 00/02/11-12:44:52 (suhler)
 *   handle comments
 *
 * 1.15 99/11/30-09:47:57 (suhler)
 *   remove diagnostics
 *
 * 1.14 99/11/16-19:08:11 (cstevens)
 *   wildcard imports.
 *
 * 1.13 99/10/28-17:23:17 (cstevens)
 *   ChangedTemplate
 *
 * 1.12 99/10/25-15:37:49 (cstevens)
 *   working on templates with symbolic names.
 *
 * 1.11 99/10/21-18:14:30 (cstevens)
 *   TemplateHandler now takes a list of Templates, rather than just one.  When
 *   parsing an HTML file, it will now dispatch to the union of all the tag
 *   methods defined in the list of Templates.  In that way, the user can
 *   compose things like the BSLTemplate to iterate over request properties with
 *   the PropsTemplate to substitute in their values.  Otherwise, it would have
 *   required N separate passes (via N separate TemplateHandlers) over the HTML
 *   file, one for each Template and/or level of recursion in the BSLTemplate.
 *
 * 1.10 99/10/14-14:57:26 (cstevens)
 *   resolve wilcard imports.
 *
 * 1.9 99/10/06-12:18:48 (suhler)
 *
 * 1.8 99/09/29-16:05:59 (cstevens)
 *   New HtmlRewriter object, that allows arbitrary rewriting of the HTML (by
 *   templates and others), instead of forcing the templates to return a string
 *   that contained all of the new HTML content in one big string.
 *
 * 1.7 99/08/04-18:44:11 (suhler)
 *   I forget, sorry
 *
 * 1.6 99/06/28-10:51:10 (suhler)
 *   separate template processing class
 *
 * 1.5 99/05/25-09:21:21 (suhler)
 *   modified to use new HtmlMunger
 *
 * 1.4 99/05/24-17:38:51 (suhler)
 *   Changed to use new HtmlMunger.  I already did this change once, but
 *   it got lost somehow ???
 *
 * 1.3 99/05/13-09:23:00 (suhler)
 *   ??
 *
 * 1.2 99/05/06-15:22:53 (suhler)
 *   added the ability to use a mapping table
 *
 * 1.2 99/05/06-14:12:25 (Codemgr)
 *   SunPro Code Manager data about conflicts, renames, etc...
 *   Name history : 2 1 handlers/templates/TemplateRunner.java
 *   Name history : 1 0 handlers/Template.java
 *
 * 1.1 99/05/06-14:12:24 (suhler)
 *   date and time created 99/05/06 14:12:24 by suhler
 *
 */

package sunlabs.brazil.template;

import sunlabs.brazil.server.Request;
import sunlabs.brazil.server.Server;
import sunlabs.brazil.session.SessionManager;
import sunlabs.brazil.util.regexp.Regexp;
import sunlabs.brazil.util.regexp.Regsub;
import sunlabs.brazil.util.LexHTML;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.Vector;
import java.util.StringTokenizer;

/**
 * Class for processing html templates.
 * An html template is an ordinary html string, with additional
 * application specific tags added (sort of like XML).  Each
 * tag is mapped to a java method of a template class, that rewrites its
 * tag into normal html.
 * 

* The mechanism used to map templates into sessions is inadaquate, and * should be fixed in a future version. In the current implementation, * Each session maintains its own set of template instances. Instance * variables in template classes may be used to hold session specific * state. Calls to a template are synchronized on the session id; * only one request per session is dealt with simultaneously. * * @author Colin Stevens * @author Stephen Uhler * @version %W */ public class TemplateRunner { static final String TAG_PREFIX = "tagPrefix"; static final String TEMPLATE = "template"; private Server server; private String prefix; // prefix of the invoker private int tagsProcessed = 0; private int tagsSeen = 0; private String error = null; // The last error private Class[] types; // Types of templates. private String[] prefixes; // Template specific prefixes private Hashtable dispatch; // Map: tag -> class index, method. private static final Regexp hex = new Regexp("_x[0-9a-zA-Z][0-9a-zA-Z]"); private static final Regexp.Filter hexFilter = new Regexp.Filter() { public boolean filter(Regsub rs, StringBuffer sb) { String match = rs.matched(); int hi = Character.digit(match.charAt(2), 16); int lo = Character.digit(match.charAt(3), 16); sb.append((char) ((hi << 4) | lo)); return true; } }; private static class Dispatch { int index; Method method; String prefix; public Dispatch(int index, Method method, String prefix) { this.index = index; this.method = method; this.prefix = prefix; } } /** * Process an HTML template with a template processing class. * We peruse the template class, looking at all of its methods. * When when we process a template, we match the html tags against * the declared methods in the template class. Each * method name of the form tag_xxx or * tag_slash_xxx is invoked when the corrosponding * <xxx> or </xxx> tag is found. *

* Each instance of _xnn in the method name * is replaced by the corrosponding hex code for the character. * This allows non-printable tags to to be processed with templates. *

* The methods init and done are each called * once, at the beginning and end of the page respectively. These methods * are called for all templates, in the order they are specified in * the templates parameter. *

* There are three methods that may be defined that don't follow the naming * convention described above. They are: *

    *
  • comment
    * is called for each html/XML comment. *
  • string
    * is called for all text between any tags. *
  • defaultTag
    * is called for every tag that does not specifically have a tag method. * If more than one template defines one of these methods, only the * first template's method will be called. *
*

* If the server property "tagPrefix" associated with each template's * properties prefix exists, it is used to prefix each tag name * (this feature is for experimental support of XML namespaces, * and probably doesn't belong here). *

* @param server * The HTTP server that created the Handler or * Filter that invoked this * TemplateRunner * @param prefix * The prefix associated with the parent Handler * or Filter * @param names * The names of the Template classes or property prefixes (i.e. * tokens) that, when concatenated with ".class" define a property * that names a Template class. This TemplateRunner will * dispatch to the methods described by the union of all the * tag methods in the given Template classes. *

* The init and done methods for each * template specified will be called in order. If any of * the calls returns false, this handler terminates * and no output is generated. *

* The names "comment", "string", and "defaultTag" are * handled specially. */ public TemplateRunner(Server server, String prefix, String names) throws ClassNotFoundException, ClassCastException { this.server = server; this.prefix = prefix; dispatch = new Hashtable(); Vector types = new Vector(); Vector prefixes = new Vector(); int count = 0; StringTokenizer st = new StringTokenizer(names); while (st.hasMoreTokens()) { String temPrefix = null; String temName = st.nextToken(); // System.out.println("Processing template: " + temName); String className = server.props.getProperty(temName + ".class"); if (className == null) { className = temName; temPrefix = prefix; } else { temPrefix = temName + "."; } Class temType = Class.forName(className.trim()); if (TemplateInterface.class.isAssignableFrom(temType) == false) { throw new ClassCastException(temType.getName()); } types.addElement(temType); prefixes.addElement(temPrefix); Method[] methods = temType.getMethods(); String tagPrefix = server.props.getProperty(temPrefix + TAG_PREFIX, server.props.getProperty(prefix + TAG_PREFIX, "")); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; String name = method.getName(); if (name.equals("comment") && dispatch.get(name) == null) { dispatch.put(name, new Dispatch(count, method, temPrefix)); // System.out.println("Found comment in: " + temName); continue; } if (name.equals("string") && dispatch.get(name) == null) { dispatch.put(name, new Dispatch(count, method, temPrefix)); // System.out.println("Found string in: " + temName); continue; } if (name.equals("defaultTag") && dispatch.get(name) == null) { dispatch.put(name, new Dispatch(count, method, temPrefix)); // System.out.println("Found defaultTag in: " + temName); continue; } if (name.startsWith("tag_") == false) { continue; } name = name.substring(4); if (name.startsWith("slash_")) { name = "/" + tagPrefix + name.substring(6); } else { name = tagPrefix + name; } name = hex.sub(name, hexFilter); if (dispatch.get(name) == null) { dispatch.put(name, new Dispatch(count, method, temPrefix)); } } count++; } types.copyInto(this.types = new Class[count]); prefixes.copyInto(this.prefixes = new String[count]); } /** * Process an html template file, using the supplied template processing * Return the content of the template just processed, or null if * there was no template processed. * * @param content * The template. * * @param sessionId * An arbitrary identifier used to locate the correct instance * of the template class for processing this template. The * first time an identifier is used, a new instance is created. * * @param args * The arguments passed to the templates init method. * * @return content or null */ public String process(Request request, String content, String sessionId) { tagsProcessed = 0; tagsSeen = 0; // error = null; /* * Now look up the session object. If this is a new session, then we * get a new object, otherwise we get the last one we used for this * session */ Vector templates = (Vector) SessionManager.getSession(sessionId, prefix + TEMPLATE, Vector.class); synchronized (templates) { if (templates.size() != types.length) { templates.setSize(0); for (int i = 0; i < types.length; i++) { try { templates.addElement(types[i].newInstance()); } catch (Exception e) { throw new ClassCastException("Missing constructor " + types[i].getName() + "()"); } } } RewriteContext hr = new RewriteContext(server, prefix, request, content, sessionId, this, templates); /* * Call the init() method of all the Templates. */ for (int i = 0; i < types.length; i++) { Template obj = (Template) templates.elementAt(i); hr.prefix = prefixes[i]; // see discussion in RewriteContext if (obj.init(hr) == false) { error = types[i] + " " + request.url + ": init Rejecting request"; return null; } } /* * Process the document. */ while (hr.nextToken()) { process(hr); } /* * Call the done() method of all the Templates. */ for (int i = 0; i < templates.size(); i++) { Template obj = (Template) templates.elementAt(i); hr.prefix = prefixes[i]; // see discussion in RewriteContext if (obj.done(hr) == false) { error = types[i] + " " + request.url + ": done rejecting request"; return null; } } return hr.toString(); } } /** * Processes the next token in the HTML document represented by the * given RewriteContext. Processing a token involves either * dispatching to a tag-handling method in one of the * Template objects, or just advancing to the next token * if no Template was interested. * * @param hr * The RewriteContext holding the state of the HTML document. */ public void process(RewriteContext hr) { switch (hr.getType()) { case LexHTML.COMMENT: case LexHTML.STRING: case LexHTML.TAG: { String tag; if (hr.getType() == LexHTML.COMMENT) { tag = "comment"; // System.out.println("(comment)"); } else if (hr.getType() == LexHTML.STRING) { tag = "string"; } else { tag = hr.getTag(); tagsSeen++; } Dispatch d = (Dispatch) dispatch.get(tag); if (hr.getType() == LexHTML.TAG && d == null) { d = (Dispatch) dispatch.get("defaultTag"); } if (d != null) { Template obj = (Template) hr.templates.elementAt(d.index); // see discussion in RewriteContext regarding prefix hr.prefix = d.prefix; try { d.method.invoke(obj, hr.args); tagsProcessed++; } catch (InvocationTargetException e) { hr.append(""); e.getTargetException().printStackTrace(); } catch (Exception e) { hr.append(""); e.printStackTrace(); } } } } } /** * Return the last error message generated, or null of no * errors have occurred since the last call to "process". * XXX not thread safe between calls to process() and getError(). */ public String getError() { return error; } /** * Return the # of HTML tags seen in the previous call to "process". */ public int tagsSeen() { return tagsSeen; } /** * Return the # of tags replaced in the previous call to "process". */ public int tagsProcessed() { return tagsProcessed; } /** * Return the object instance of the template that will process this * tag (if any). This allows templates to cooperate with each other. * If you need to use this method, then one of us did something wrong. */ protected Template templateFromTag(RewriteContext hr, String tag) { Template template = null; Dispatch d = (Dispatch) dispatch.get(tag); if (d != null) { template = (Template) hr.templates.elementAt(d.index); } return template; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy