KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jimm > datavision > Formula


1 package jimm.datavision;
2 import jimm.datavision.field.Field;
3 import jimm.datavision.field.SpecialField;
4 import jimm.datavision.source.Column;
5 import jimm.util.StringUtils;
6 import jimm.util.XMLWriter;
7 import jimm.util.Replacer;
8 import jimm.util.I18N;
9
10 /**
11  * A formula is a Bean Scripting Framework (BSF) script evaluated at runtime.
12  * It may contain database column values, other formulas, special values, and
13  * aggregates.
14  * <p>
15  * Before being evaluated, the following substitutions are made withing
16  * the evaluation string of a formula:
17  * <ul>
18  * <li>{<i>table_name.column_name</i>} is replaced by the current value
19  * of the column <i>table_name.column_name</i>, but only if the column exists.
20  * If it doesn't, then the entire "{...}" text is kept as-is.</li>
21  * <li>{&#64;<i>id_number</i>} is replaced by the results of evaluating the
22  * formula whose id is <i>id_number</i>.</li>
23  * <li> {%<i>special_value_name</i>} is replaced by a special value
24  * (report title, report run date, page number, or record number).</li>
25  * <li> {?<i>id_number</i>} is replaced by a parameter value (string,
26  * number, or date).</li>
27  * <li> {!<i>id_number</i>} is replaced by a user column's value (string,
28  * number, or date).</li>
29  * <ul>
30  *
31  * @author Jim Menard, <a HREF="mailto:jimm@io.com">jimm@io.com</a>
32  */

33 public class Formula extends Expression {
34
35 protected String JavaDoc language;
36 protected Object JavaDoc cachedEvalResult;
37 protected boolean useCache;
38 protected boolean shouldEvaluate;
39 protected boolean showException;
40
41 /**
42  * Constructor.
43  *
44  * @param id the unique identifier for the new formula; if <code>null</code>,
45  * generate a new id
46  * @param report the report containing this formula
47  * @param name the formula name
48  */

49 public Formula(Long JavaDoc id, Report report, String JavaDoc name) {
50     this(id, report, name, null);
51     useCache = false;
52     shouldEvaluate = true;
53     showException = true;
54 }
55
56 /**
57  * Constructor. If <i>id</i> is <code>null</code>, generates a new id number.
58  * This number is one higher than any previously-seen id number. This does
59  * <em>not</em> guarantee that no later formula will be created manually with
60  * the same id number.
61  *
62  * @param id the unique identifier for the new formula; if <code>null</code>,
63  * generate a new id
64  * @param report the report containing this formula
65  * @param name the formula name
66  * @param evalString the string to evaulate at runtime.
67  */

68 public Formula(Long JavaDoc id, Report report, String JavaDoc name, String JavaDoc evalString) {
69     super(id == null ? report.generateNewFormulaId() : id, report, name,
70       evalString, "#");
71     language = report.getScripting().getDefaultLanguage();
72 }
73
74
75 public String JavaDoc dragString() {
76     return "formula:" + getId();
77 }
78
79 public String JavaDoc designLabel() {
80     return "{@" + getName() + "}";
81 }
82
83 public String JavaDoc formulaString() {
84     return "{@" + getId() + "}";
85 }
86
87 /**
88  * Tells this formula to evaluate only when <var>shouldEvaluate</var> is
89  * <code>true</code> and to return the cached value otherwise.
90  */

91 public void useCache() { useCache = true; }
92
93 /**
94  * Tells this formula to evaluate the BSF script the next time
95  * <code>eval</code> is called.
96  */

97 public void shouldEvaluate() { shouldEvaluate = true; }
98
99 public void setExpression(String JavaDoc newExpression) {
100     super.setExpression(newExpression);
101     showException = true;
102 }
103
104 /**
105  * Evaluate this formula and return the result as either a
106  * <code>String</code> or a <code>Double</code>. Before evaluation, special
107  * fields, formulas, and column references are subtituted with their
108  * values.
109  * <p>
110  * After substitution, if the formula contains a <code>null</code> anywhere
111  * then <code>null</code> is returned.
112  *
113  * @return the result of evaluating the formula as either a
114  * <code>String</code> or a <code>Double</code>; possibly <code>null</code>
115  */

116 public Object JavaDoc eval() {
117     return eval(null);
118 }
119
120 /**
121  * Evaluate this formula and return the result. Before evaluation, special
122  * fields, formulas, and column references are subtituted with their
123  * values. <code>null</code> values are replaced by &quot;nil&quot;.
124  * <p>
125  * After substitution, if the formula contains a <code>null</code> anywhere
126  * then <code>null</code> is returned. If the BSF script throws an exception,
127  * then <code>null</code> is returned.
128  * <p>
129  * During substitution, we take care not to replace "#{...}" Ruby string
130  * substition operators.
131  * <p>
132  * The <var>formulaField</var> is used when the formula contains a
133  * &quot;group.count&quot; special field.
134  *
135  * @param formulaField the field containing this formula; may be
136  * <code>null</code>
137  * @return the result of evaluating the formula as a BSF script; possibly
138  * <code>null</code>
139  *
140  */

141 public Object JavaDoc eval(Field formulaField) {
142     if (!useCache || shouldEvaluate) {
143     cachedEvalResult = evaluate(formulaField);
144     shouldEvaluate = false;
145     }
146     return cachedEvalResult;
147 }
148
149 /**
150  * Modifies the formula text so it is ready to evaluate, then gives it to the
151  * report to evaluate and returns the result. {@link #eval} calls this method
152  * and stores the return value into <var>cachedEvalResult</var>.
153  *
154  * @param formulaField the field that is using this formula, used to
155  * evaluate any special fields in the formula; may be <code>null</code>
156  * @return the result of evaluating the formula as a BSF script; possibly
157  * <code>null</code>
158  * @see SpecialField#value
159  */

160 protected Object JavaDoc evaluate(final Field formulaField) {
161     String JavaDoc str = getExpression();
162     if (str == null || str.trim().length() == 0)
163     return null;
164
165     // Special values
166
str = StringUtils.replaceDelimited("#", "{%", "}", new Replacer() {
167     public Object JavaDoc replace(String JavaDoc str) {
168         Object JavaDoc obj = SpecialField.value(formulaField, str, report);
169         return obj == null ? "nil" : obj;
170     }},
171                str);
172     if (str == null) return null;
173
174     // Formula values
175
str = StringUtils.replaceDelimited("#", "{@", "}", new Replacer() {
176     public Object JavaDoc replace(String JavaDoc str) {
177         Formula f = report.findFormula(str);
178         return f == null ? "nil" : f.eval(formulaField);
179     }},
180                str);
181     if (str == null) return null;
182
183     // Parameter values
184
str = StringUtils.replaceDelimited("#", "{?", "}", new Replacer() {
185     public Object JavaDoc replace(String JavaDoc str) {
186         Parameter p = report.findParameter(str);
187         return p == null ? "nil" : p.getValue();
188     }},
189                str);
190     if (str == null) return null;
191
192     // User column values
193
str = StringUtils.replaceDelimited("#", "{!", "}", new Replacer() {
194     public Object JavaDoc replace(String JavaDoc str) {
195         UserColumn uc = report.findUserColumn(str);
196         return uc == null ? "nil" : report.columnValue(uc);
197     }},
198                str);
199     if (str == null) return null;
200
201     // Column values
202
str = StringUtils.replaceDelimited("#", "{", "}", new Replacer() {
203     public Object JavaDoc replace(String JavaDoc str) {
204         Column col = report.findColumn(str);
205         if (col == null)
206         return "{" + str + "}";
207
208         Object JavaDoc val = null;
209         switch (col.getType()) {
210         case java.sql.Types.CHAR:
211         case java.sql.Types.VARCHAR:
212         case java.sql.Types.DATE:
213         case java.sql.Types.TIME:
214         case java.sql.Types.TIMESTAMP:
215         val = report.columnValue(col);
216         val = val == null ? "nil" : quoted(val);
217         break;
218         default:
219         val = report.columnValue(col);
220         if (val == null)
221             val = "nil";
222         break;
223         }
224         return val;
225     }},
226                str);
227     if (str == null || str.trim().length() == 0) return null;
228
229     try {
230     return report.eval(getLanguage(), str, getName());
231     }
232     catch (Exception JavaDoc e) {
233     if (showException) {
234         showException = false;
235         // I don't pass e to error() so we avoid a stack trace, which
236
// will be almost useless to the user or to me.
237
ErrorHandler.error(I18N.get("Formula.script_error")
238                    + " \"" + str + '"' + ": " + e.toString(),
239                    I18N.get("Formula.script_error_title"));
240     }
241     return null;
242     }
243 }
244
245 /**
246  * Returns the scripting language this formula uses.
247  *
248  * @return the language to use when evaluating this formula
249  */

250 public String JavaDoc getLanguage() {
251     return language == null ? report.getScripting().getDefaultLanguage()
252     : language;
253 }
254
255 public void setLanguage(String JavaDoc newLang) {
256     if (newLang == null) newLang = report.getScripting().getDefaultLanguage();
257
258     if (!language.equals(newLang)) {
259     language = newLang;
260     setChanged();
261     notifyObservers();
262     }
263 }
264
265 /**
266  * Returns a string representation of an object enclosed in double quotes.
267  * Each double quote and backslash within the object's string
268  * representation is escaped with a backslash character.
269  *
270  * @param obj any object
271  * @return a double-quoted string representation of the object
272  */

273 protected String JavaDoc quoted(Object JavaDoc obj) {
274     String JavaDoc val = obj.toString();
275     StringBuffer JavaDoc buf = new StringBuffer JavaDoc("\"");
276     int len = val.length();
277     for (int i = 0; i < len; ++i) {
278     char c = val.charAt(i);
279     switch (c) {
280     case '"': case '\\':
281         buf.append('\\');
282         buf.append(c);
283         break;
284     default:
285         buf.append(c);
286         break;
287     }
288     }
289     buf.append('"');
290     return buf.toString();
291 }
292
293 /**
294  * If <var>obj</var> is a <code>String</code> and it is surrounded by
295  * double quotes, return an unquoted copy. All doubled double quotes
296  * are turned into single double quotes.
297  *
298  * @param obj any object
299  * @return <var>obj</var> if it is not a <code>String</code>, an unquoted
300  * copy if it is a <code>String</code>
301  */

302 public Object JavaDoc unquoted(Object JavaDoc obj) {
303     if (obj instanceof String JavaDoc) {
304     String JavaDoc str = (String JavaDoc)obj;
305     if (str.startsWith("\"") && str.endsWith("\"")) {
306         str = str.substring(1, str.length() - 1);
307         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
308         int oldPos = 0;
309         for (int pos = str.indexOf("\\"); pos != -1;
310          pos = str.indexOf("\\", oldPos))
311         {
312         buf.append(str.substring(oldPos, pos));
313         buf.append(str.charAt(pos + 1));
314         oldPos = pos + 2;
315         }
316         buf.append(str.substring(oldPos));
317         return buf.toString();
318     }
319     }
320     return obj;
321 }
322
323 public void writeXML(XMLWriter out) {
324     writeXML(out, "formula");
325 }
326
327 protected void writeAdditionalAttributes(XMLWriter out) {
328     if (language != null && language.length() != 0
329     && !language.equals(report.getScripting().getDefaultLanguage()))
330     out.attr("language", language);
331     
332 }
333
334 }
335
Popular Tags