/* * MacroTemplate.java * * Brazil project web application toolkit, * export version: 2.3 * Copyright (c) 2001-2008 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): suhler. * * Version: 2.11 * Created by suhler on 01/07/11 * Last modified by suhler on 08/07/24 16:35:51 * * Version Histories: * * 2.11 08/07/24-16:35:51 (suhler) * preserve token accumulation * * 2.10 05/08/25-10:53:34 (suhler) * added "isSingleton" and "args" variables to macros * * 2.9 05/06/16-07:58:44 (suhler) * make sure quotes are removed from tag names * * 2.8 04/05/28-10:33:31 (suhler) * add "subst=true" for macros in startup file * * 2.7 04/05/24-15:13:29 (suhler) * remove left-over diagostics * * 2.6 04/05/21-12:28:36 (suhler) * - The initial macros file is now interpreted as an xml template, and not * a java properties format file [WARNING: backward compatibility] * - main() may be used to convert the old style macro initialization tables * (which no one used 'cause they were too hadr to generate) into the new * format. * * 2.5 03/07/07-14:02:14 (suhler) * use addClosingTag() convenience macro * * 2.4 02/12/19-11:37:15 (suhler) * checkpoint * * 2.3 02/11/14-14:28:06 (suhler) * handle empty bodies * * 2.2 02/11/05-15:10:12 (suhler) * Accepted child'\''s version in workspace "/home/suhler/brazil/naws". * * 1.5.1.1 02/11/05-15:07:56 (suhler) * Complete re-do of the macro template. * Hopefully, this one will work better * * 2.1 02/10/01-16:36:51 (suhler) * version change * * 1.5 02/07/24-10:46:39 (suhler) * doc updates * * 1.4 02/01/29-14:53:38 (suhler) * doc lint * * 1.3 01/07/17-14:17:32 (suhler) * add initial macro file processing * * 1.2 01/07/16-16:49:36 (suhler) * use new Template convenience methods * * 1.2 01/07/11-20:12:42 (Codemgr) * SunPro Code Manager data about conflicts, renames, etc... * Name history : 1 0 handlers/templates/MacroTemplate.java * * 1.1 01/07/11-20:12:41 (suhler) * date and time created 01/07/11 20:12:41 by suhler * */ package sunlabs.brazil.template; import java.io.InputStream; import java.io.IOException; import java.util.Properties; import sunlabs.brazil.handler.ResourceHandler; import sunlabs.brazil.server.Server; import sunlabs.brazil.session.SessionManager; import sunlabs.brazil.util.Format; import sunlabs.brazil.util.LexML; import java.util.Enumeration; /** * Template class for defining macros. Macros are defined by: *
 * <definemacro name=macro-name [global=true|false]> .... </definemacro>
 * 
* The text (.....) has any leading and trailing whitespace * removed. *

* To expand a macro: <macro-name name1=value1 ...>. * All ${...} constructs in The previously saved macro body * are processed, and the result replaces the macro-name * tag. Attributes provided in the macro name override any variables * that exist in the request properties. *

* Several special variables are supplied, as if they were specified * as attributes, but only if otherwise not already defined * either as an attribute or as a property, *

*
isSingleton *
is set to "true" or "false" to indicate the tag has been specified * as a singleton. *
args *
is the string of un-parsed arguments to this macro. *
*

* Templates are processed by reading the input "document" a tag * at a time to generate the output document. * By default, the macro body is pushed onto the not-yet-processed * input stream; any tags contained in the macro body will be processed. * If the defer attribute is present, * the macro body is placed onto the output stream instead. * This is more efficient, but requires an additional filter pass * if the body of the macro contains tags that need to be processed * in the current context. *

*

*
init *
The name of the file (or resource) to read a default set of * macro definitions from. If an * absolute path isn't specified, the file is taken relative to * the document root. The default macros are kept in the * SessionManager on a per-server basis. All macros defined * in the "init" file are global. All markup in this file outside * of a macro definition is ignored. If "subst" is present as * an attribute of a macro definition in this file, then all * ${...} are evaluated relative to "server.props" before the * macro is defined. *
subst *
If specified, then any tags that are not processed by any * templates will have all ${..} contructs in attribute * values substituted. This subsumes the function of the * "SubstAllTemplate". *
*

* This is an experiment. The current implementation is flawed, although * it is less flawed than the previous one. *

* NOTE:
* The init files in previous releases used java properties format * files to define macros; This version uses xml templates. See "main" * below for a utility to convert the old properties format files to * the new format. * * @author Stephen Uhler * @version MacroTemplate.java 2.11 */ public class MacroTemplate extends Template { public Properties macroTable=null; // session specific definitions Properties initial=null; // initial and global properties boolean shouldSubst = false; /** * Read in the inital macros, if needed. */ public boolean init(RewriteContext hr) { hr.addClosingTag("definemacro"); shouldSubst = (hr.request.props.getProperty(hr.prefix + "subst") != null); // Load in initial macros if macro table doesn't yet exist if (macroTable == null) { initial = (Properties) SessionManager.getSession(hr.prefix, "Macros", Properties.class); String init = hr.request.props.getProperty(hr.prefix + "init"); if (init != null && initial.isEmpty()) { loadInitial(hr, init); } macroTable = new Properties(initial); } return super.init(hr); } /** * load initial macros, if any. * XXX We should load from an XML file, not a properties file. */ void loadInitial_old(RewriteContext hr, String init) { try { InputStream in = ResourceHandler.getResourceStream( hr.request.props, hr.prefix, init); initial.load(in); in.close(); hr.request.log(Server.LOG_DIAGNOSTIC, hr.prefix, "loading initial macros: " + initial.toString()); } catch (IOException e) { hr.request.log(Server.LOG_WARNING, hr.prefix, "Can't find macro init file: " + init); } } /** * Load initial macros from xml file. All markup outside of * the "definemacro" and "/definemacro" tags are ignored * If a "subst" attribute is found, then evaluate all ${..} against * server.props before saving the macro body. "subst" only applies * for the "init" macro table. */ void loadInitial(RewriteContext hr, String init) { String src = null; try { src = ResourceHandler.getResourceString( hr.request.props, hr.prefix, init); } catch (IOException e) { hr.request.log(Server.LOG_WARNING, hr.prefix, "Can't find macro init file: " + init); } LexML lex = new LexML(src); while (lex.nextToken()) { if (lex.getType()==LexML.TAG && lex.getTag().equals("definemacro")) { String name=Format.deQuote(lex.getAttributes().get("name")); if (name==null || name.trim().equals("")) { continue; } boolean doSubst = (lex.getAttributes().get("subst") != null); String value = snarfTillClose(lex, "definemacro").trim(); if (doSubst) { value = Format.subst(hr.server.props, value); } if (!value.equals("")) { initial.put(name, value); hr.request.log(Server.LOG_DIAGNOSTIC, hr.prefix, "initial macro: [" + name + "] (" + value.length() + " chars)"); } else { initial.remove(name); } } else { // System.out.println("Skipping tag: " + lex.getToken()); } } // initial.save(System.out, hr.prefix + " initial macros"); } /** * Grab all the markup starting from the current tag until * the matching closing tag, and return as a string. */ public static String snarfTillClose(LexML lex, String tag) { if (!lex.isSingleton()) { StringBuffer sb = new StringBuffer(); String start = "<" + tag + " "; String end = ""; // XXX not quite int nest = 0; // set nesting level sb = new StringBuffer(); while (lex.nextToken()) { String t = lex.getToken(); lex.getTag(); // force singleton checking! if (t.startsWith(start) && !lex.isSingleton()) { nest++; } else if (t.equals(end) && (--nest < 0)) { break; } sb.append(t); } return sb.toString(); } else { return ""; } } /** * Define a new macro. use "name" as the macro name. Once defined, the tag * <name ...> will be replaced by the contents of the macro * named "name". *

     * <definemacro name=nnn [global=true|false]>...</definemacro>
     * 
* */ public void tag_definemacro(RewriteContext hr) { String name = hr.get("name"); boolean global = hr.isTrue("global"); if (name == null) { name = hr.getArgs(); } if (name == null) { return; } debug(hr); boolean was = hr.accumulate(false); hr.nextToken(); String body = hr.getBody().trim(); hr.accumulate(was); Properties p = global ? initial : macroTable; if (body.equals("")) { p.remove(name); hr.request.log(Server.LOG_DIAGNOSTIC, hr.prefix, "removing macro: " + name); } else { p.put(name, body); // XXX we should keep formal parameter list for defaults here hr.request.log(Server.LOG_DIAGNOSTIC, hr.prefix, "creating macro: [" + name + "] (" + body.length() + (p==initial ? ") global" : ") local")); } hr.nextToken(); // eat the /definemacro debug(hr); hr.killToken(); } /** * Run the macro, push formal parameters on the stack first. * This examimes all non-processed tags to see if they are macros, * and processes those that are. *

* If the parameter defer=true is present, the text of the macro is output * directly, with only ${..} substitutions performed. Otherwise, the markup in the macro * body is rescanned and processed. */ public void defaultTag(RewriteContext hr) { String tag = hr.getTag(); String script = macroTable.getProperty(tag); if (script != null) { debug(hr); String body = Format.subst(new Props(hr), script); if (!hr.isTrue("defer")) { String rest = hr.lex.rest(); if (rest != null) { hr.lex.replace(body + rest); } else { hr.lex.replace(body); } hr.killToken(); } else { hr.append(body); } } else if (shouldSubst) { Enumeration e = hr.keys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); hr.put(key, hr.get(key)); } } } /** * Special Properties class for use with format.subst() that will * return any formal parameters first, then look in request props. */ static class Props extends Properties { RewriteContext hr; String args = null; Props(RewriteContext hr) { this.hr=hr; } public String getProperty(String key) { String value = hr.get(key); if (value == null && (value = hr.request.props.getProperty(key)) == null) { if (key.equals("isSingleton")) { value = hr.isSingleton() ? "true" : "false"; } else if (key.equals("args")) { return hr.getArgs(); } } return value; } } /** * Convert stdin properties format macro definition files * to the new template style. Use this to update to the new * initial macro template format. */ public static void main(String args[]) throws IOException { Properties in = new Properties(); in.load(System.in); System.out.println(""); Enumeration e = in.keys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); String value = in.getProperty(key); System.out.println("\n"); System.out.println(" " + value); System.out.println(""); } } }