/* * Format.java * * Brazil project web application toolkit, * export version: 2.3 * Copyright (c) 2000-2009 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: cstevens. * Portions created by cstevens are Copyright (C) Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): cstevens, drach, suhler. * * Version: 2.8 * Created by cstevens on 00/04/17 * Last modified by suhler on 09/02/12 14:03:02 * * Version Histories: * * 2.8 09/02/12-14:03:02 (suhler) * add stringToInt utility method, should replace most inline version with it * * 2.7 06/11/13-15:08:02 (suhler) * add "unsubst()" for protecting html * * 2.6 05/06/16-08:00:31 (suhler) * add public String dequoting method * * 2.5 04/11/30-14:59:57 (suhler) * fixed sccs version string * * 2.4 04/04/28-15:54:50 (suhler) * added \s for spaces and \v for ' (\q was taken) * * 2.3 03/08/01-16:17:14 (suhler) * fixes for javadoc * * 2.2 03/07/10-09:24:59 (suhler) * Add static methods to check for the boolean value of a string * * 2.1 02/10/01-16:36:58 (suhler) * version change * * 1.18 01/10/16-15:59:54 (suhler) * add "\t" for TAB * * 1.17 01/09/03-20:34:46 (suhler) * add \r to esape list * * 1.16 01/08/28-20:39:59 (suhler) * added \a \l and \g for & < and > * * 1.15 01/08/20-16:38:25 (suhler) * lint * * 1.14 01/07/19-20:58:41 (suhler) * doc lint * * 1.13 01/07/18-10:40:03 (suhler) * allow escape behavior to be spicified on a per-call basis * * 1.12 01/07/16-16:54:17 (suhler) * rationalize escaping mechanism, remove unused code * * 1.11 01/06/29-11:08:25 (drach) * Hopefully, easier to understand substitution mechanism * * 1.10 01/03/23-14:50:03 (cstevens) * * 1.9 01/01/11-17:28:52 (cstevens) * Merged changes between child workspace "/home/cstevens/ws/brazil/naws" and * parent workspace "/export/ws/brazil/naws". * * 1.6.1.1 01/01/11-17:22:24 (cstevens) * default values in properties (temporary) * * 1.8 01/01/07-19:07:01 (suhler) * allow escaping of ${...} with $\{...\} * * 1.7 00/12/18-11:24:14 (suhler) * allow ${...} to be escaped. * * 1.6 00/12/11-13:33:30 (suhler) * doc typo * * 1.5 00/12/05-13:18:05 (suhler) * cleanup * * 1.4 00/10/05-15:52:16 (cstevens) * PropsTemplate.subst() and PropsTemplate.getProperty() moved to the Format * class. * * 1.3 00/04/26-16:14:47 (suhler) * doc update * * 1.2 00/04/17-16:40:17 (cstevens) * Truncating result * * 1.2 00/04/17-15:01:38 (Codemgr) * SunPro Code Manager data about conflicts, renames, etc... * Name history : 1 0 util/Format.java * * 1.1 00/04/17-15:01:37 (cstevens) * date and time created 00/04/17 15:01:37 by cstevens * */ package sunlabs.brazil.util; import java.util.Dictionary; import java.util.Properties; import java.util.StringTokenizer; /** * Format a string by substituting values into it * from a properties object. * * @author colin stevens * @author stephen uhler */ public class Format { /** * Allow a property name to contain the value of another * property, permitting nested variable substitution in attribute * values. The name of the embedded property to be substituted is * bracketted by "${" and "}". See {@link #subst}. *
* "ghi" => "foo"
* "deffoojkl" => "baz"
* "abcbazmno" => "garply"
* * getProperty("ghi") => "foo"
* getProperty("def${ghi}jkl") => "baz"
* getProperty("abc${def${ghi}jkl}mno") => "garply"
*
* * @param props * The table of variables to use when substituting. * * @param expr * The property name, prossibly containing substitutions. * * @param defaultValue * The value to use if the top-level substitution does not * exist. May be null. */ public static String getProperty(Properties props, String expr, String defaultValue) { return props.getProperty(subst(props, expr), defaultValue); } /** * Allow a tag attribute value to contain the value of another * property, permitting nested variable substitution in attribute * values. To escape ${XXX}, use \${XXX}. *

* The sequence "\X" is identical to "X", except when "X" is one of: *

*
$
A literal "$", that will not introduce a variable * substitution if it is followed by "{". *
n
Insert a NL (newline) character *
r
Insert a CR (Carriage return) character *
"
Insert a single quote ("). *
l
Insert a (<). *
g
Insert a (>). *
a
Insert a (&). *
end of value
Insert a "\". *
*

*

* "ghi" = "foo"
* "deffoojkl" = "baz"
* "abcbazmno" = "garply"
* * subst("ghi") = "ghi"
* subst("def${ghi}jkl") = "deffoojkl"
* subst("def\${ghi}jkl") = "def${ghi}jkl"
* subst("abc${def${ghi}jkl}mno") = "abcbazmno"
* subst("${abc${def${ghi}jkl}mno}") = "garply"
*
* * @param props The table of variables to substitute. * If this is a Properties object, then the * getProperty() method is used instead of the * Dictionary class get() method. * @param str The expression containing the substitutions. * Embedded property names, bracketted by "${" and "}" * are looked up in the props table and replaced with * their value. Nested substitutions are allowed. * * @return The substituted string. If a variable is not * found in the table, the empty string is used. */ public static String subst(Dictionary props, String str) { return subst(props, str, false); } /** * Allow a tag attribute value to contain the value of another * property, permitting nested variable substitution in attribute * values. To escape ${XXX}, use \${XXX}. * See {@link #subst(Dictionary props, String str) above}. *

* if noEsc is true, then * The sequence "\X" is identical to "\X" for all X except X=$. */ public static String subst(Dictionary props, String str, boolean noEsc) { if (str == null) { return null; } return subst(props, new Chars(str), noEsc); } private static String subst(Dictionary dict, Chars chars, boolean esc) { StringBuffer sb = new StringBuffer(); char c; char save; String result; String value; loop: while (true) { c = chars.get(); switch (c) { case Chars.NUL: break loop; case '$': c = chars.get(); switch (c) { case Chars.NUL: sb.append('$'); break loop; case '{': save = chars.setend('}'); result = subst(dict, chars, esc); chars.setend(save); value = getProperty(dict, result); sb.append(value); break; default: chars.pushback(); sb.append('$'); break; } break; case '\\': c = chars.getraw(); if (c == Chars.NUL) { sb.append('\\'); break loop; } if (esc) { // only \$ is special switch (c) { case '$': break; default: chars.pushback(); c = '\\'; break; } } else { // other stuff is special too switch (c) { case 'a': c = '&'; break; case 'g': c = '>'; break; case 'l': c = '<'; break; case 'q': c = '"'; break; case 's': c = ' '; break; case 'v': c = '\''; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; default: break; } } sb.append(c); break; default: sb.append(c); break; } } return sb.toString(); } private static String getProperty(Dictionary dict, String name) { int hash = name.indexOf('#'); String def = ""; if (hash >= 0) { def = name.substring(hash + 1); name = name.substring(0, hash); } Object obj; if (dict instanceof Properties) { obj = ((Properties) dict).getProperty(name); } else { obj = dict.get(name); } String value = (obj == null) ? null : obj.toString(); if (value == null || value.length() == 0) value = def; return value; } /** * See if a String represents a "true" boolean value, which consists of: * "yes", "true", "on", or "1", in any case. */ public static boolean isTrue(String s) { if (s != null) { String v = s.trim().toLowerCase(); return v.equals("true") || v.equals("yes") || v.equals("on") || v.equals("1"); } return false; } /** * See if a String represents a "false" boolean value, which consists of: * "no", "false", "off", or "0", in any case. */ public static boolean isFalse(String s) { if (s != null) { String v = s.trim().toLowerCase(); return v.equals("false") || v.equals("no") || v.equals("off") || v.equals("0"); } return false; } /** * Convert a string to an integer; use a default value if the * conversion fails. * @param value the value to attemp conversion of * @param dftl The result if the value is not a valid number * @return The integer value of "value", or "dflt" if value is NAN. */ public static int stringToInt(String value, int dflt) { try { return Integer.parseInt(value); } catch (NumberFormatException e) { return dflt; } } /** * Remove surrounding quotes (" or ') from a string. */ public static String deQuote(String str) { int len; if (str==null || (len=str.length()) < 2) { return str; } char ch = str.charAt(0); if (((ch == '"') || (ch == '\'')) && (str.charAt(len-1) == ch)) { return str.substring(1, len-1); } return str; } /** * Make an html string suitable for including as an attribute value. * Convert '<', '>', '&', '"', and ''' to \l, \g, \a, \q and \a. */ public static String unsubst(String data) { String TOKENS = "<>&\"\'\n\t"; StringTokenizer st = new StringTokenizer(data, TOKENS, true); StringBuffer sb = new StringBuffer(); while (st.hasMoreTokens()) { String token = st.nextToken(); if (token.length() > 0) { sb.append(token); } else { switch(token.charAt(0)) { case '<': sb.append("\\l"); break; case '>': sb.append("\\g"); break; case '&': sb.append("\\a"); break; case '\"': sb.append("\\q"); break; case '\'': sb.append("\\v"); break; case '\n': sb.append("\\n"); break; case '\t': sb.append("\\t"); break; default: sb.append(token); break; } } } return sb.toString(); } } class Chars { public static final char NUL = '\0'; private int i; private char end; private char[] chars; Chars(String s) { i = 0; end = NUL; chars = s.toCharArray(); } char getraw() { if (i >= chars.length) return NUL; return chars[i++]; } char get() { char c = getraw(); if (c == end) return NUL; return c; } void pushback() { if (--i < 0) i = 0; } char setend(char end) { char save = this.end; this.end = end; return save; } }