KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jimm > datavision > Expression


1 package jimm.datavision;
2 import jimm.datavision.field.Field;
3 import jimm.util.XMLWriter;
4 import jimm.util.StringUtils;
5 import jimm.util.Replacer;
6 import jimm.datavision.source.Column;
7 import jimm.util.I18N;
8 import java.util.*;
9
10 /**
11  * The abstract superclass of objects that are evaluated, such as formulas
12  * and user columns. An expression contains text that is evaluated. The
13  * text may contain database column values, formulas, special values,
14  * or other types of objects.
15  * <p>
16  * Before being evaluated, the following substitutions are made withing
17  * the evaluation string:
18  * <ul>
19  * <li>{<i>table_name.column_name</i>} is replaced by the current value
20  * of the column <i>table_name.column_name</i>.</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 abstract class Expression
34     extends Observable
35     implements Identity, Nameable, Writeable, Draggable, Observer
36 {
37
38 protected Long JavaDoc id;
39 protected Report report;
40 protected String JavaDoc name;
41 protected String JavaDoc expr;
42 protected String JavaDoc exceptAfter;
43 protected ArrayList observedContents;
44
45 /**
46  * Given a string, returns a string with all instances of formula,
47  * parameter, and user column "formula strings" replaced by "display name"
48  * strings. If there are no such strings, the original string is returned.
49  *
50  * @return a string with all formula strings replaced by display name
51  * strings
52  */

53 public static String JavaDoc expressionToDisplay(Report report, String JavaDoc str) {
54     if (str == null || str.length() == 0 || str.indexOf("{") == -1)
55     return str;
56
57     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
58     int len = str.length();
59     for (int i = 0; i < len; ++i) {
60     char c = str.charAt(i);
61     if (c == '{' && (i + 1) < len) {
62         int nameStart, nameEnd;
63         switch (str.charAt(i + 1)) {
64         case '@': // Formula
65
nameStart = i + 2;
66         nameEnd = str.indexOf("}", nameStart);
67         if (nameEnd != -1) {
68             String JavaDoc idAsString = str.substring(nameStart, nameEnd);
69             buf.append("{@");
70             buf.append(report.findFormula(idAsString).getName());
71             buf.append("}");
72             i = nameEnd;
73         }
74         break;
75         case '?': // Parameter
76
nameStart = i + 2;
77         nameEnd = str.indexOf("}", nameStart);
78         if (nameEnd != -1) {
79             String JavaDoc idAsString = str.substring(nameStart, nameEnd);
80             buf.append("{?");
81             buf.append(report.findParameter(idAsString).getName());
82             buf.append("}");
83             i = nameEnd;
84         }
85         break;
86         case '!': // User column
87
nameStart = i + 2;
88         nameEnd = str.indexOf("}", nameStart);
89         if (nameEnd != -1) {
90             String JavaDoc idAsString = str.substring(nameStart, nameEnd);
91             buf.append("{!");
92             buf.append(report.findUserColumn(idAsString).getName());
93             buf.append("}");
94             i = nameEnd;
95         }
96         break;
97         default:
98         buf.append(c);
99         break;
100         }
101     }
102     else {
103         buf.append(c);
104     }
105     }
106     return buf.toString();
107 }
108
109 /**
110  * Given a string, returns a string with all instances of formula,
111  * parameter, and user column "display names" replaced by "formula
112  * strings". If there are no such strings, the original string is returned.
113  *
114  * @param report a report
115  * @param str a string with display names
116  * @return a string with all display names replaced by formula strings
117  */

118 public static String JavaDoc displayToExpression(Report report, String JavaDoc str) {
119     if (str == null || str.length() == 0 || str.indexOf("{") == -1)
120     return str;
121
122     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
123     int len = str.length();
124     for (int i = 0; i < len; ++i) {
125     char c = str.charAt(i);
126     if (c == '{' && (i + 1) < len) {
127         int nameStart, nameEnd;
128         switch (str.charAt(i + 1)) {
129         case '@': // Formula
130
nameStart = i + 2;
131         nameEnd = str.indexOf("}", nameStart);
132         if (nameEnd != -1) {
133             String JavaDoc formulaName = str.substring(nameStart, nameEnd);
134             buf.append("{@");
135             Formula formula = report.findFormulaByName(formulaName);
136             if (formula == null) {
137             str = I18N.get("Utils.in")
138                 + " \"" + str + "\": "
139                 + I18N.get("Utils.no_such_formula")
140                 + ' ' + formulaName;
141             throw new IllegalArgumentException JavaDoc(str);
142             }
143             buf.append(formula.getId());
144             buf.append("}");
145             i = nameEnd;
146         }
147         break;
148         case '?': // Parameter
149
nameStart = i + 2;
150         nameEnd = str.indexOf("}", nameStart);
151         if (nameEnd != -1) {
152             String JavaDoc paramName = str.substring(nameStart, nameEnd);
153             buf.append("{?");
154             Parameter param = report.findParameterByName(paramName);
155             if (param == null) {
156             str = I18N.get("Utils.in")
157                 + " \"" + str + "\": "
158                 + I18N.get("Utils.no_such_param")
159                 + ' ' + paramName;
160             throw new IllegalArgumentException JavaDoc(str);
161             }
162             buf.append(param.getId());
163             buf.append("}");
164             i = nameEnd;
165         }
166         break;
167         case '!': // User column
168
nameStart = i + 2;
169         nameEnd = str.indexOf("}", nameStart);
170         if (nameEnd != -1) {
171             String JavaDoc ucName = str.substring(nameStart, nameEnd);
172             buf.append("{!");
173             UserColumn uc = report.findUserColumnByName(ucName);
174             if (uc == null) {
175             str = I18N.get("Utils.in")
176                 + " \"" + str + "\": "
177                 + I18N.get("Utils.no_such_usercol")
178                 + ' ' + ucName;
179             throw new IllegalArgumentException JavaDoc(str);
180             }
181             buf.append(uc.getId());
182             buf.append("}");
183             i = nameEnd;
184         }
185         break;
186         default:
187         buf.append(c);
188         break;
189         }
190     }
191     else {
192         buf.append(c);
193     }
194     }
195
196     return buf.toString();
197 }
198
199 /**
200  * Constructor. If <i>id</i> is <code>null</code>, throws an
201  * <code>IllegalArgumentException</code>. This is because subclasses are
202  * responsible for generating their id number. For example, formulas call
203  * <code>Report.generateNewFormulaId</code>.
204  *
205  * @param id the unique identifier for the new expression; may not be
206  * <code>null</code>
207  * @param report the report containing this expression
208  * @param name the expression name
209  * @param expression the string to evaulate at runtime; may be
210  * <code>null</code>
211  * @param exceptAfter when looking for things inside "{}" braces, ignore
212  * braces immediately after this string
213  */

214 protected Expression(Long JavaDoc id, Report report, String JavaDoc name, String JavaDoc expression,
215              String JavaDoc exceptAfter)
216 {
217     if (id == null) // Need not use I18N; this is a programmer err
218
throw new IllegalArgumentException JavaDoc("Subclasses of Expression must"
219                        + " not pass in a null id");
220
221     this.report = report;
222     this.id = id;
223     this.name = name;
224     expr = expression;
225     this.exceptAfter = exceptAfter;
226
227     // I'd like to start observing the contents of the eval string here,
228
// but the other expressions may not yet be defined (for example, when
229
// reading in expressions from an XML file). That's why we start observing
230
// contents when someone asks for the eval string.
231
observedContents = null;
232 }
233
234 protected void finalize() throws Throwable JavaDoc {
235     stopObservingContents();
236     super.finalize();
237 }
238
239 public void update(Observable o, Object JavaDoc arg) {
240     setChanged();
241     notifyObservers(arg);
242 }
243
244 public Object JavaDoc getId() { return id; }
245
246 /**
247  * Returns the name for this expression.
248  *
249  * @return the name
250  */

251 public String JavaDoc getName() { return name; }
252
253 /**
254  * Sets the name.
255  *
256  * @param newName the new name
257  */

258 public void setName(String JavaDoc newName) {
259     if (name != newName && (name == null || !name.equals(newName))) {
260     name = newName;
261     setChanged();
262     notifyObservers();
263     }
264 }
265
266 /**
267  * Returns the expression string.
268  *
269  * @return the eval string
270  */

271 public String JavaDoc getExpression() {
272     // I'd like to start observing the contents of the eval string as soon
273
// as we are constructed, but the other expressions may not yet be defined
274
// (for example, when reading in expressions from an XML file). That's why
275
// we start observing contents when someone asks for the eval string.
276
if (observedContents == null)
277     startObservingContents();
278
279     return expr;
280 }
281
282 /**
283  * Sets the eval string.
284  *
285  * @param newExpression the new eval string
286  */

287 public void setExpression(String JavaDoc newExpression) {
288     if (expr != newExpression
289     && (expr == null || !expr.equals(newExpression)))
290     {
291     stopObservingContents();
292     expr = newExpression;
293
294     // Don't start observing contents yet. Wait until someone calls
295
// getExpression(). See the comment there.
296
// startObservingContents();
297

298     setChanged();
299     notifyObservers();
300     }
301 }
302
303 /**
304  * Starts observing all observables referenced by this expression: formulas,
305  * parameters, and user columns.
306  */

307 protected void startObservingContents() {
308     observedContents = new ArrayList(); // Even if expr is null
309

310     if (expr == null || expr.length() == 0)
311     return;
312
313     // Here, we are using replacers so we can start observing things.
314
// Usually, they are used to replace strings.
315

316     // Formulas
317
StringUtils.replaceDelimited(exceptAfter, "{@", "}", new Replacer() {
318     public Object JavaDoc replace(String JavaDoc str) {
319         Formula f = report.findFormula(str);
320         observedContents.add(f);
321         f.addObserver(Expression.this);
322         return ""; // Avoid early bail-out
323
}},
324                expr);
325
326     // Parameters
327
StringUtils.replaceDelimited(exceptAfter, "{?", "}", new Replacer() {
328     public Object JavaDoc replace(String JavaDoc str) {
329         Parameter p = report.findParameter(str);
330         observedContents.add(p);
331         p.addObserver(Expression.this);
332         return ""; // Avoid early bail-out
333
}},
334                expr);
335
336     // User columns
337
StringUtils.replaceDelimited(exceptAfter, "{!", "}", new Replacer() {
338     public Object JavaDoc replace(String JavaDoc str) {
339         UserColumn uc = report.findUserColumn(str);
340         observedContents.add(uc);
341         uc.addObserver(Expression.this);
342         return ""; // Avoid early bail-out
343
}},
344                expr);
345 }
346
347 /**
348  * Stops observing that which we were observing.
349  */

350 protected void stopObservingContents() {
351     if (observedContents != null) {
352     for (Iterator iter = observedContents.iterator(); iter.hasNext(); )
353         ((Observable)iter.next()).deleteObserver(this);
354     observedContents = null;
355     }
356 }
357
358 /**
359  * Returns the expression string fit for human consumption. This mainly means
360  * that we substitute formula, parameter, and user column numbers with names.
361  * Called from any expression editor. This code assumes that curly braces are
362  * never nested.
363  *
364  * @return the eval string with formula, parameter, and user column id numbers
365  * replaced with names
366  */

367 public String JavaDoc getEditableExpression() {
368     return expressionToDisplay(report, getExpression());
369 }
370
371 /**
372  * Sets the eval string after replacing formula, parameter, and user column
373  * names with their id numbers. Called from a editor.
374  * <p>
375  * This method will throw an <code>IllegalArgumentException</code> if any
376  * formula, parameter, or user column name is not the name of some existing
377  * object.
378  *
379  * @param newExpression the new eval string
380  * @throws IllegalArgumentException
381  */

382 public void setEditableExpression(String JavaDoc newExpression) {
383     setExpression(displayToExpression(report, newExpression));
384 }
385
386 public abstract String JavaDoc dragString();
387 public abstract String JavaDoc designLabel();
388 public abstract String JavaDoc formulaString();
389
390 /**
391  * Returns <code>true</code> if this expression contains a reference to the
392  * specified field.
393  *
394  * @param f a field
395  * @return <code>true</code> if this field contains a reference to the
396  * specified field
397  */

398 public boolean refersTo(Field f) {
399     String JavaDoc str = getExpression();
400     if (str != null && str.length() > 0)
401     return str.indexOf(f.formulaString()) != -1;
402     else
403     return false;
404 }
405
406 /**
407  * Returns <code>true</code> if this expression contains a reference to the
408  * specified expression (formula or user column).
409  *
410  * @param expression an expression
411  * @return <code>true</code> if this field is the same as or contains a
412  * reference to the specified expression
413  */

414 public boolean refersTo(Expression expression) {
415     String JavaDoc str = getExpression();
416     if (str != null && str.length() > 0)
417     return str.indexOf(expression.formulaString()) != -1;
418     else
419     return false;
420 }
421
422 /**
423  * Returns <code>true</code> if this expression contains a reference to the
424  * specified parameter.
425  *
426  * @param p a parameter
427  * @return <code>true</code> if this field contains a reference to the
428  * specified parameter
429  */

430 public boolean refersTo(Parameter p) {
431     String JavaDoc str = getExpression();
432     if (str != null && str.length() > 0)
433     return str.indexOf(p.formulaString()) != -1;
434     else
435     return false;
436 }
437
438 /**
439  * Returns a collection of the columns used in the expression. This is used
440  * by the report's query when it is figuring out what columns and tables
441  * are used by the report.
442  *
443  * @return a possibly empty collection of database columns
444  * @see jimm.datavision.source.Query#findSelectablesUsed
445  */

446 public Collection columnsUsed() {
447     final ArrayList list = new ArrayList();
448
449     // We are using a replacer passively, to look for curly-delimited
450
// expressions. Nothing in the expression text gets modified.
451
StringUtils.replaceDelimited(exceptAfter, "{", "}", new Replacer() {
452     public Object JavaDoc replace(String JavaDoc str) {
453         switch (str.charAt(0)) {
454         case '!': // User column
455
UserColumn uc = report.findUserColumn(str.substring(1));
456         if (uc != null) // Should never be null
457
list.addAll(uc.columnsUsed());
458         break;
459         case '%': // Special field
460
case '@': // Formula
461
case '?': // Parameter
462
break; // ...all are ignored
463
default:
464         Column col = report.findColumn(str);
465         if (col != null) // May be null if language uses braces
466
list.add(col);
467         }
468         return ""; // So we don't quit early
469
}},
470                getExpression());
471
472     return list;
473 }
474
475 /**
476  * Returns a collection of the user columns used in the expression. This
477  * is used by the report's query when it is figuring out what columns,
478  * tables, and user columns are used by the report.
479  *
480  * @return a possibly empty collection of user columns
481  * @see jimm.datavision.source.Query#findSelectablesUsed
482  */

483 public Collection userColumnsUsed() {
484     final ArrayList list = new ArrayList();
485
486     // We are using a replacer passively, to look for curly-delimited
487
// expressions. Nothing in the expression text gets modified.
488
StringUtils.replaceDelimited(exceptAfter, "{!", "}", new Replacer() {
489     public Object JavaDoc replace(String JavaDoc str) {
490         UserColumn uc = report.findUserColumn(str);
491         if (uc != null) // Should never be null
492
list.add(uc);
493         return ""; // So we don't bail out
494
}},
495                getExpression());
496
497     return list;
498 }
499
500 /**
501  * Writes this expression as an XML tag.
502  *
503  * @param out a writer that knows how to write XML
504  */

505 public abstract void writeXML(XMLWriter out);
506
507 protected void writeXML(XMLWriter out, String JavaDoc elementName) {
508     out.startElement(elementName);
509     out.attr("id", id);
510     out.attr("name", name);
511     writeAdditionalAttributes(out);
512     out.cdata(getExpression());
513     out.endElement();
514 }
515
516 /**
517  * Writes additional attributes. Default behavior is to do nothing.
518  *
519  * @param out a writer that knows how to write XML
520  */

521 protected void writeAdditionalAttributes(XMLWriter out) {}
522
523 }
524
Popular Tags