001: /*
002: * Format.java
003: *
004: * Brazil project web application Framework,
005: * export version: 1.1
006: * Copyright (c) 2000-2001 Sun Microsystems, Inc.
007: *
008: * Sun Public License Notice
009: *
010: * The contents of this file are subject to the Sun Public License Version
011: * 1.0 (the "License"). You may not use this file except in compliance with
012: * the License. A copy of the License is included as the file "license.terms",
013: * and also available at http://www.sun.com/
014: *
015: * The Original Code is from:
016: * Brazil project web application Framework release 1.1.
017: * The Initial Developer of the Original Code is: cstevens.
018: * Portions created by cstevens are Copyright (C) Sun Microsystems, Inc.
019: * All Rights Reserved.
020: *
021: * Contributor(s): cstevens, suhler.
022: *
023: * Version: 1.9
024: * Created by cstevens on 00/04/17
025: * Last modified by cstevens on 01/01/11 17:28:52
026: */
027:
028: package sunlabs.brazil.util;
029:
030: import java.util.Dictionary;
031: import java.util.Properties;
032:
033: /**
034: * Format a string by substituting values into it,
035: * either from an array of strings, or a properties object.
036: *
037: * @author colin stevens
038: * @author stephen uhler
039: */
040:
041: public class Format {
042:
043: /**
044: * Foreach instance of %i (i = 1...9) in the format parameter,
045: * replace it with the value of ith element of the
046: * array, by calling its toString method.
047: *
048: * @param format String containing %n... sequences
049: * @param array The array of values to substitute into
050: * the format string.
051: */
052:
053: public static String fmt(String format, Object[] array) {
054: return fmt(format, array, null);
055: }
056:
057: /**
058: * Foreach instance of %key% in the format parameter,
059: * replace it with the value of key in the properties, or the
060: * empty string if key is not found.
061: * @param format String containing %key...% sequences
062: * @param props Properties to match the keys,
063: * and replace them with their corrosponding values.
064: */
065:
066: public static String fmt(String format, Properties props) {
067: return fmt(format, null, props);
068: }
069:
070: public static String fmt(String format, Object[] array,
071: Properties props) {
072: int i = format.indexOf('%');
073: if (i < 0) {
074: return format;
075: }
076: int len = format.length();
077: StringBuffer result = new StringBuffer(format.substring(0, i));
078: for (; i < len; i++) {
079: try {
080: char ch = format.charAt(i);
081: if (ch == '%') {
082: i++;
083: ch = format.charAt(i);
084: if (ch == '%') {
085: result.append(ch);
086: } else if ((ch >= '1') && (ch <= '9')) {
087: result.append(array[ch - '1']);
088: } else {
089: int index = format.indexOf('%', i);
090: String key = format.substring(i, index);
091: result.append((String) props.get(key));
092: i = index;
093: }
094: } else {
095: result.append(ch);
096: }
097: } catch (IndexOutOfBoundsException e) {
098: /* Ignore malformed "%" sequences */
099: } catch (NullPointerException e) {
100: /* Ignore non-existent properties or array */
101: }
102: }
103: return result.toString();
104: }
105:
106: /**
107: * Allow a property name to contain the value of another
108: * property, permitting nested variable substitution in attribute
109: * values. The name of the embedded property to be substituted is
110: * bracketted by "${" and "}". See {@link #subst}.
111: * <blockquote>
112: * "ghi" => "foo"
113: * "deffoojkl" => "baz"
114: * "abcbazmno" => "garply"
115: *
116: * getProperty("ghi") => "foo"
117: * getProperty("def${ghi}jkl") => "baz"
118: * getProperty("abc${def${ghi}jkl}mno") => "garply"
119: * </blockquote>
120: *
121: * @param props
122: * The table of variables to use when substituting.
123: *
124: * @param expr
125: * The property name, prossibly containing substitutions.
126: *
127: * @param defaultValue
128: * The value to use if the top-level substitution does not
129: * exist. May be <code>null</code>.
130: */
131: public static String getProperty(Properties props, String expr,
132: String defaultValue) {
133: String result = props.getProperty(subst(props, expr));
134: if (result == null) {
135: result = defaultValue;
136: }
137: return result;
138: }
139:
140: /**
141: * Allow a tag attribute value to contain the value of another
142: * property, permitting nested variable substitution in attribute
143: * values. To escape ${XXX}, use $\{XXX\}. The "\" is only special
144: * when it is in front of "{" or "}" (This behavior is unfortunate,
145: * and may change in the future).
146: * <p>
147: * <blockquote>
148: * "ghi" => "foo"
149: * "deffoojkl" => "baz"
150: * "abcbazmno" => "garply"
151: *
152: * subst("ghi") => "ghi"
153: * subst("def${ghi}jkl") => "deffoojkl"
154: * subst("abc${def${ghi}jkl}mno") => "abcbazmno"
155: * subst("${abc${def${ghi}jkl}mno}") => "garply"
156: * </blockquote>
157: *
158: * @param props The table of variables to substitute.
159: * If this is a Properties object, then the
160: * getProperty() method is used instead of the
161: * Dictionary class get() method.
162: * @param str The expression containing the substitutions.
163: * Embedded property names, bracketted by "${" and "}"
164: * are looked up in the props table and replaced with
165: * their value. Nested substitutions are allowed.
166: *
167: * @returns The substituted string. If a variable is not
168: * found in the table, the empty string is used.
169: */
170:
171: public static String subst(Dictionary props, String str) {
172: if (str == null) {
173: return null;
174: }
175: return subst(props, str, 0, new int[1]);
176: }
177:
178: private static String subst(Dictionary dict, String str, int off,
179: int[] endPtr) {
180: int len = str.length();
181: int i = off;
182:
183: StringBuffer sb = new StringBuffer();
184: try {
185: for (; i < len; i++) {
186: char ch = str.charAt(i);
187: if ((ch == '$') && (str.charAt(i + 1) == '{')) {
188: String name = subst(dict, str, i + 2, endPtr);
189: int dots = name.indexOf('#');
190: String def = "";
191: if (dots >= 0) {
192: def = name.substring(dots + 1);
193: name = name.substring(0, dots);
194: }
195: Object obj;
196: if (dict instanceof Properties) {
197: obj = ((Properties) dict).getProperty(name);
198: } else {
199: obj = dict.get(name);
200: }
201: String value = (obj == null) ? null : obj
202: .toString();
203: if ((value == null) || (value.length() == 0)) {
204: value = def;
205: }
206: sb.append(value);
207: i = endPtr[0];
208: continue;
209: } else if (ch == '\\'
210: && (i + 1) < len
211: && (str.charAt(i + 1) == '{' || str
212: .charAt(i + 1) == '}')) {
213: ch = str.charAt(++i);
214: } else if (ch == '}') {
215: break;
216: }
217: sb.append(ch);
218: }
219: } catch (StringIndexOutOfBoundsException e) {
220: // Ran off end of string after seeing '$' but no '('.
221: }
222:
223: endPtr[0] = i;
224: return sb.toString();
225: }
226: }
|