KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jimm > datavision > Parameter


1 package jimm.datavision;
2 import jimm.util.XMLWriter;
3 import jimm.util.I18N;
4 import java.util.Observable JavaDoc;
5 import java.util.ArrayList JavaDoc;
6 import java.util.Iterator JavaDoc;
7 import java.util.Date JavaDoc;
8 import java.io.StringWriter JavaDoc;
9 import java.text.SimpleDateFormat JavaDoc;
10 import java.text.ParsePosition JavaDoc;
11
12 /**
13  * A parameter is a piece of data the value of which is determined by
14  * asking the user each time a report runs. Default values are only used
15  * when asking the user for values, not when generating values in
16  * <code>getValue</code>.
17  * <p>
18  * I started out with subclasses for each type of parameter. The problem is,
19  * the user gets to pick what kind of data the parameter holds and that
20  * type can be changed any time after the parameter gets created. Therefore,
21  * we hold objects and change our output based on the type of the data.
22  *
23  * @author Jim Menard, <a HREF="mailto:jimm@io.com">jimm@io.com</a>
24  */

25 public class Parameter
26     extends Observable JavaDoc
27     implements Identity, Nameable, Writeable, Draggable, Cloneable JavaDoc
28 {
29
30 public static final int TYPE_BOOLEAN = 0;
31 public static final int TYPE_STRING = 1;
32 public static final int TYPE_NUMERIC = 2;
33 public static final int TYPE_DATE = 3;
34
35 public static final int ARITY_ONE = 0;
36 public static final int ARITY_RANGE = 1;
37 public static final int ARITY_LIST_SINGLE = 2;
38 public static final int ARITY_LIST_MULTIPLE = 3;
39
40 protected static SimpleDateFormat JavaDoc formatter =
41     new SimpleDateFormat JavaDoc("yyyy-MM-dd");
42 protected static ParsePosition JavaDoc parsePosition = new ParsePosition JavaDoc(0);
43
44 protected Long JavaDoc id;
45 protected Report report;
46 protected String JavaDoc name;
47 protected String JavaDoc question;
48 protected int type;
49 protected int arity;
50 protected ArrayList JavaDoc defaultValues;
51 protected ArrayList JavaDoc values;
52
53 /**
54  * Constructor. Creates a string parameter with no name or question
55  * string.
56  *
57  * @param id the unique identifier for the new parameter; if
58  * <code>null</code>, generate a new id
59  * @param report the report in which this parameter resides
60  */

61 public Parameter(Long JavaDoc id, Report report)
62 {
63     this(id, report, "string", "", "", "single");
64 }
65
66 /**
67  * Constructor.
68  * <p>
69  * If <i>id</i> is <code>null</code>, generates a new id number. This number
70  * is one higher than any previously-seen id number. This does <em>not</em>
71  * guarantee that no later parameter will be created manually with the same
72  * id number.
73  *
74  * @param id the unique identifier for the new parameter; if
75  * <code>null</code>, generate a new id
76  * @param report the report in which this parameter resides
77  * @param typeName one of "string", "numeric", or "date"; found in report XML
78  * @param name the name of this parameter
79  * @param question the question to ask when getting the parameter's value
80  * from the user
81  * @param arityString arity (single, range, list) as a string
82  */

83 public Parameter(Long JavaDoc id, Report report, String JavaDoc typeName, String JavaDoc name,
84             String JavaDoc question, String JavaDoc arityString)
85 {
86     this.report = report;
87
88     // Convert type name to type number.
89
if (typeName == null || typeName.length() == 0) {
90     String JavaDoc str = I18N.get("Parameter.param_cap") + " " + id + ": "
91         + I18N.get("Parameter.missing_type");
92     throw new IllegalArgumentException JavaDoc(str);
93     }
94
95     typeName = typeName.toLowerCase().trim();
96     if ("boolean".equals(typeName)) type = TYPE_BOOLEAN;
97     else if ("string".equals(typeName)) type = TYPE_STRING;
98     else if ("numeric".equals(typeName)) type = TYPE_NUMERIC;
99     else if ("date".equals(typeName)) type = TYPE_DATE;
100     else {
101     String JavaDoc str = I18N.get("Parameter.param_cap") + " " + id + ": "
102         + I18N.get("Parameter.illegal_type");
103     throw new IllegalArgumentException JavaDoc(str);
104     }
105
106     this.name = name;
107     this.question = question;
108
109     // Convert arity string ("range", "list", or "single").
110
if (arityString == null || arityString.length() == 0) {
111     String JavaDoc str = I18N.get("Parameter.param_cap") + id + ": "
112         + I18N.get("Parameter.missing_arity");
113     throw new IllegalArgumentException JavaDoc(str);
114     }
115     arityString = arityString.toLowerCase().trim();
116     if ("single".equals(arityString)) arity = ARITY_ONE;
117     else if ("range".equals(arityString)) arity = ARITY_RANGE;
118     else if ("list-single".equals(arityString)) arity = ARITY_LIST_SINGLE;
119     else if ("list-multiple".equals(arityString)) arity = ARITY_LIST_MULTIPLE;
120     else {
121     String JavaDoc str = I18N.get("Parameter.param_cap") + id + ": "
122         + I18N.get("Parameter.illegal_arity");
123     throw new IllegalArgumentException JavaDoc(str);
124     }
125
126     initialize(id);
127 }
128
129 /**
130  * Constructor.
131  * <p>
132  * If <i>id</i> is <code>null</code>, generates a new id number. This number
133  * is one higher than any previously-seen id number. This does <em>not</em>
134  * guarantee that no later parameter will be created manually with the same
135  * id number.
136  *
137  * @param id the unique identifier for the new parameter; if
138  * <code>null</code>, generate a new id
139  * @param report the report in which this parameter resides
140  * @param type one of
141  * <code>TYPE_BOOLEAN</code>, <code>TYPE_STRING</code>,
142  * <code>TYPE_NUMERIC</code>, or <code>TYPE_DATE</code>
143  * @param name the name of this parameter
144  * @param question the name of this parameter
145  * @param arity one of <code>ARITY_ONE</code>, <code>ARITY_RANGE</code>,
146  * <code>ARITY_LIST_SINGLE</code>, or <code>ARITY_LIST_MULTIPLE</code>
147  */

148 public Parameter(Long JavaDoc id, Report report, int type, String JavaDoc name,
149             String JavaDoc question, int arity)
150 {
151     this.report = report;
152     this.type = type;
153     this.name = name;
154     this.question = question;
155     this.arity = arity;
156
157     initialize(id);
158 }
159
160 private void initialize(Long JavaDoc id) {
161     if (id == null)
162     id = report.generateNewParameterId();
163     this.id = id;
164
165     // Check for legal combinations of type and arity
166
switch (type) {
167     case TYPE_BOOLEAN:
168     if (arity != ARITY_ONE) {
169         String JavaDoc str = I18N.get("Parameter.param_cap") + id + ": "
170         + I18N.get("Parameter.yesno_single");
171         throw new IllegalArgumentException JavaDoc(str);
172     }
173     break;
174     case TYPE_DATE:
175     if (arity != ARITY_ONE && arity != ARITY_RANGE) {
176         String JavaDoc str = I18N.get("Parameter.param_cap") + id + ": "
177         + I18N.get("Parameter.date_arity_err");
178         throw new IllegalArgumentException JavaDoc(str);
179     }
180     break;
181     }
182
183     defaultValues = new ArrayList JavaDoc();
184     values = new ArrayList JavaDoc();
185 }
186
187 public Object JavaDoc clone() {
188     Parameter p = new Parameter(null, report, type, name, question, arity);
189     for (Iterator JavaDoc iter = defaultValues.iterator(); iter.hasNext(); ) {
190     Object JavaDoc obj = iter.next();
191     if (obj instanceof Boolean JavaDoc)
192         p.defaultValues.add(obj);
193     else if (obj instanceof String JavaDoc)
194         p.defaultValues.add(new String JavaDoc((String JavaDoc)obj));
195     else if (obj instanceof Number JavaDoc)
196         p.defaultValues.add(obj);
197     else if (obj instanceof Date JavaDoc)
198         p.defaultValues.add(((Date JavaDoc)obj).clone());
199     }
200     return p;
201 }
202
203 public Object JavaDoc getId() { return id; }
204
205 /**
206  * Returns the name for this parameter.
207  *
208  * @return the name
209  */

210 public String JavaDoc getName() { return name; }
211
212 /**
213  * Sets the name.
214  *
215  * @param newName the new name
216  */

217 public void setName(String JavaDoc newName) {
218     if (name != newName && (name == null || !name.equals(newName))) {
219     name = newName;
220     setChanged();
221     notifyObservers();
222     }
223 }
224
225 /**
226  * Returns the question for this parameter.
227  *
228  * @return the question
229  */

230 public String JavaDoc getQuestion() { return question; }
231
232 /**
233  * Sets the question.
234  *
235  * @param newQuestion the new question
236  */

237 public void setQuestion(String JavaDoc newQuestion) {
238     if (question != newQuestion
239     && (question == null || !question.equals(newQuestion)))
240     {
241     question = newQuestion;
242     setChanged();
243     notifyObservers();
244     }
245 }
246
247 /**
248  * Returns the type of this field. Will be one of
249  * <code>TYPE_BOOLEAN</code>, <code>TYPE_STRING</code>,
250  * <code>TYPE_NUMERIC</code>, or <code>TYPE_DATE</code>.
251  *
252  * @return the type number
253  */

254 public int getType() { return type; }
255
256 /**
257  * Sets the parameter type. Must be one of
258  * <code>TYPE_BOOLEAN</code>, <code>TYPE_STRING</code>,
259  * <code>TYPE_NUMERIC</code>, or <code>TYPE_DATE</code>. If the new type
260  * is different than the old, we also make sure the arity is appropriate
261  * (for example, no boolean lists) and clear the value and default value
262  * lists.
263  *
264  * @param newType the new type; must be one of <code>TYPE_BOOLEAN</code>,
265  * <code>TYPE_STRING</code>, <code>TYPE_NUMERIC</code>, or
266  * <code>TYPE_DATE</code>
267  */

268 public void setType(int newType) {
269     if (type != newType) {
270     type = newType;
271
272     defaultValues.clear();
273     values.clear();
274
275     if (type == TYPE_BOOLEAN) {
276         if (arity != ARITY_ONE)
277         arity = ARITY_ONE;
278     }
279     else if (type == TYPE_DATE) {
280         if (arity == ARITY_LIST_SINGLE || arity == ARITY_LIST_MULTIPLE)
281         arity = ARITY_ONE;
282     }
283
284     setChanged();
285     notifyObservers();
286     }
287 }
288
289 /**
290  * Returns the arity of this field. Will be one of <code>ARITY_ONE</code>,
291  * <code>ARITY_RANGE</code>, <code>ARITY_LIST_SINGLE</code>, or
292  * <code>ARITY_LIST_MULTIPLE</code>.
293  *
294  * @return the arity number
295  */

296 public int getArity() { return arity; }
297
298 /**
299  * Returns <code>true</code> if the specified combination of type and arity
300  * are legal.
301  *
302  * @param aType one of <code>TYPE_BOOLEAN</code>, <code>TYPE_STRING</code>,
303  * <code>TYPE_NUMERIC</code>, or <code>TYPE_DATE</code>
304  * @param anArity one of <code>ARITY_ONE</code>, <code>ARITY_RANGE</code>,
305  * <code>ARITY_LIST_SINGLE</code>, or <code>ARITY_LIST_MULTIPLE</code>
306  * @return <code>true</code> if the specified combination of type and arity
307  * are legal
308  */

309 public boolean isLegal(int aType, int anArity) {
310     switch (aType) {
311     case TYPE_BOOLEAN:
312     return anArity == ARITY_ONE;
313     case TYPE_DATE:
314     return anArity != ARITY_LIST_SINGLE && anArity != ARITY_LIST_MULTIPLE;
315     case TYPE_STRING:
316     case TYPE_NUMERIC:
317     default:
318     return true;
319     }
320 }
321
322 /**
323  * Sets the parameter arity. Must be one of <code>ARITY_ONE</code>,
324  * <code>ARITY_RANGE</code>, <code>ARITY_LIST_SINGLE</code>, or
325  * <code>ARITY_LIST_MULTIPLE</code>. We disallow illegal arity values.
326  * For example, if our type is boolean we disallow a list arity.
327  *
328  * @param newArity one of <code>ARITY_ONE</code>, <code>ARITY_RANGE</code>,
329  * <code>ARITY_LIST_SINGLE</code>, or <code>ARITY_LIST_MULTIPLE</code>
330  */

331 public void setArity(int newArity) {
332     if (arity != newArity) {
333
334     if (type == TYPE_BOOLEAN) {
335         if (newArity != ARITY_ONE) {
336         String JavaDoc str = I18N.get("Parameter.param_cap") + id + ": "
337             + I18N.get("Parameter.yesno_single");
338         throw new IllegalArgumentException JavaDoc(str);
339         }
340     }
341     else if (type == TYPE_DATE) {
342         if (newArity == ARITY_LIST_SINGLE
343         || newArity == ARITY_LIST_MULTIPLE)
344         {
345         String JavaDoc str = I18N.get("Parameter.param_cap") + id + ": "
346             + I18N.get("Parameter.date_arity_err");
347         throw new IllegalArgumentException JavaDoc(str);
348         }
349     }
350
351     arity = newArity;
352
353     defaultValues.clear();
354     values.clear();
355
356     setChanged();
357     notifyObservers();
358     }
359 }
360
361 /**
362  * Returns an iterator over the default values for this parameter.
363  *
364  * @return an interator
365  */

366 public Iterator JavaDoc defaultValues() { return defaultValues.iterator(); }
367
368 /**
369  * Returns the i'th defaultValue for this parameter. If none has been
370  * assigned, create and return -- but do not store -- a reasonable default.
371  * The default is obtained by calling {@link #getDefaultForType}.
372  *
373  * @param i the index
374  * @return the defaultValue
375  */

376 public Object JavaDoc getDefaultValue(int i) {
377     Object JavaDoc val;
378     if (i < 0 || i >= defaultValues.size()
379     || (val = defaultValues.get(i)) == null)
380     {
381     return getDefaultForType(type);
382     }
383     else
384     return val;
385 }
386
387 /**
388  * Returns the default value for a specific parameter type. This is not
389  * the same as the i'th default value; it is called when you have a
390  * parameter that has no value or default value, or when you have one
391  * with a different type and you want to switch types.
392  *
393  * @param type one of <code>TYPE_BOOLEAN</code>, <code>TYPE_STRING</code>,
394  * <code>TYPE_NUMERIC</code>, or <code>TYPE_DATE</code>
395  * @return a new object appropriate for the type
396  */

397 public Object JavaDoc getDefaultForType(int type) {
398     switch (type) {
399     case TYPE_BOOLEAN: return Boolean.valueOf(false);
400     case TYPE_STRING: return "";
401     case TYPE_NUMERIC: return new Integer JavaDoc(0);
402     case TYPE_DATE: return new Date JavaDoc();
403     default:
404         String JavaDoc str = I18N.get("Paramter.illegal_type_value");
405         throw new IllegalArgumentException JavaDoc(str + " " + type);
406     }
407 }
408
409 /**
410  * Erases all default values.
411  */

412 public void removeDefaultValues() {
413     if (defaultValues.size() > 0) {
414     defaultValues.clear();
415     setChanged();
416     notifyObservers();
417     }
418 }
419
420 /**
421  * Adds a default value to the list.
422  *
423  * @param newDefaultValue a new default value
424  */

425 public void addDefaultValue(Object JavaDoc newDefaultValue) {
426     // Make sure newDefaultValue is of proper type for the values we hold
427
newDefaultValue = convertType(newDefaultValue);
428
429     defaultValues.add(newDefaultValue);
430     setChanged();
431     notifyObservers();
432 }
433
434 /**
435  * Sets the i'th defaultValue. If <var>i</var> is out of range,
436  * the list of default values grows to fit.
437  *
438  * @param i the index
439  * @param newDefaultValue a value
440  */

441 public void setDefaultValue(int i, Object JavaDoc newDefaultValue) {
442     // Make sure newDefaultValue is of proper type for the values we hold
443
newDefaultValue = convertType(newDefaultValue);
444
445     Object JavaDoc defaultValue = null;
446     if (i < defaultValues.size())
447     defaultValue = getDefaultValue(i);
448     if (defaultValue != newDefaultValue
449     && (defaultValue == null || !defaultValue.equals(newDefaultValue)))
450     {
451     defaultValues.add(i, newDefaultValue);
452     setChanged();
453     notifyObservers();
454     }
455 }
456
457 /**
458  * Returns an iterator over the values for this parameter.
459  *
460  * @return an interator
461  */

462 public Iterator JavaDoc values() { return values.iterator(); }
463
464 /**
465  * Returns the parameter value(s) the user has previously specified. If
466  * the parameter has one value, return that value or possibly null. Else,
467  * return a copy of our list of values.
468  *
469  * @return values (see description)
470  */

471 public Object JavaDoc getValue() {
472     switch (arity) {
473     case ARITY_ONE:
474     case ARITY_LIST_SINGLE:
475     return getValue(0);
476     case ARITY_RANGE:
477     ArrayList JavaDoc list = new ArrayList JavaDoc();
478     list.add(getValue(0));
479     list.add(getValue(1));
480     return list;
481     case ARITY_LIST_MULTIPLE:
482     return values.clone();
483     }
484     return null; // Will never happen
485
}
486
487 /**
488  * Returns the current value or, if that is <code>null</code>, the default
489  * value. If the index is out of range, return <code>null</code>.
490  *
491  * @param i the index
492  * @return the current or default value.
493  */

494 public Object JavaDoc getValue(int i) {
495     Object JavaDoc val = null;
496     if (i < values.size())
497     val = values.get(i);
498     if (val == null) {
499     if (i < defaultValues.size())
500         val = defaultValues.get(i);
501     }
502     return val;
503 }
504
505 /**
506  * Adds a value to the list.
507  *
508  * @param newValue a new value
509  */

510 public void addValue(Object JavaDoc newValue) {
511     values.add(convertType(newValue));
512     setChanged();
513     notifyObservers();
514 }
515
516 /**
517  * Erases all values.
518  */

519 public void removeValues() {
520     if (values.size() > 0) {
521     values.clear();
522     setChanged();
523     notifyObservers();
524     }
525 }
526
527 /**
528  * Sets the <var>i</var>'th value. If <var>i</var> is out of range,
529  * the list of values grows to fit.
530  *
531  * param i the index
532  * @param newValue the new value
533  */

534 public void setValue(int i, Object JavaDoc newValue) {
535     // Make sure newValue is of proper type for the values we hold
536
values.add(i, convertType(newValue));
537
538     setChanged();
539     notifyObservers();
540 }
541
542 /**
543  * Converts the specified object to the proper type for this parameter.
544  * Whenever we add or set a value or default value, we convert it to the
545  * proper type (string, date, etc.)
546  * <p>
547  * If our type is boolean and the incoming object is:
548  * <ul>
549  * <li>A string, return
550  * a <code>true Boolean</code> if the value matches "true", "t", "yes",
551  * or "y" (ignoring case).
552  * <li>A number, return a <code>true Boolean</code> if the value is non-zero.
553  * <li>Anything else, return a <code>true Boolean</code> (any better
554  * suggestions?)
555  *
556  * @param val any old object
557  * @return some object of the proper type
558  */

559 protected Object JavaDoc convertType(Object JavaDoc val) {
560     if (val == null)
561     return null;
562
563     switch (type) {
564     case TYPE_BOOLEAN: // Return value as a boolean
565
if (val instanceof Boolean JavaDoc)
566         return val;
567     else if (val instanceof String JavaDoc) {
568         val = ((String JavaDoc)val).toLowerCase().trim();
569         if ("true".equals(val) || "t".equals(val)
570         || "yes".equals(val) || "y".equals(val))
571         return Boolean.valueOf(true);
572         else
573         return Boolean.valueOf(false);
574     }
575     else if (val instanceof Number JavaDoc) {
576         return Boolean.valueOf(((Number JavaDoc)val).doubleValue() == 0);
577     }
578     else {
579         return Boolean.valueOf(true); // What to do here?
580
}
581     case TYPE_STRING: // Return value as a string
582
return val.toString();
583     case TYPE_NUMERIC: // Return value as a number
584
if (val instanceof Number JavaDoc)
585         return val;
586     else { // Convert val to string, then to number
587
String JavaDoc str = val.toString();
588         if (str.length() == 0)
589         return new Integer JavaDoc(0);
590         else if (str.indexOf(".") == -1)
591         return new Integer JavaDoc(str);
592         else
593         return new Double JavaDoc(str);
594     }
595     case TYPE_DATE: // Return value as a date
596
if (val instanceof Date JavaDoc)
597         return val;
598     else { // Convert val to string, then to date
599
String JavaDoc str = val.toString();
600         if (str.length() == 0)
601         return new Date JavaDoc();
602         else {
603         parsePosition.setIndex(0);
604         return formatter.parse(str, parsePosition);
605         }
606     }
607     default: // Should never happen
608
return null;
609     }
610 }
611
612 /**
613  * Returns the string used as the "type" attribute when writing this
614  * parameter as XML.
615  *
616  * @return the "type" attribute string
617  */

618 protected String JavaDoc typeString() {
619     switch (type) {
620     case TYPE_BOOLEAN: return "boolean";
621     case TYPE_STRING: return "string";
622     case TYPE_NUMERIC: return "numeric";
623     case TYPE_DATE: return "date";
624     default: return "unknown"; // Should never happen
625
}
626 }
627
628 public String JavaDoc dragString() {
629     return "parameter:" + getId();
630 }
631
632 public String JavaDoc designLabel() {
633     return "{?" + getName() + "}";
634 }
635
636 public String JavaDoc formulaString() {
637     return "{?" + getId() + "}";
638 }
639
640 /**
641  * Writes this parameter as an XML tag.
642  *
643  * @param out a writer that knows how to write XML
644  */

645 public void writeXML(XMLWriter out) {
646     String JavaDoc arityString = null;
647     switch (arity) {
648     case ARITY_ONE: arityString = "single"; break;
649     case ARITY_RANGE: arityString = "range"; break;
650     case ARITY_LIST_SINGLE: arityString = "list-single"; break;
651     case ARITY_LIST_MULTIPLE: arityString = "list-multiple"; break;
652     }
653
654     out.startElement("parameter");
655     out.attr("id", id);
656     out.attr("type", typeString());
657     out.attr("name", name);
658     out.attr("question", question);
659     out.attr("arity", arityString);
660
661     for (Iterator JavaDoc iter = defaultValues.iterator(); iter.hasNext(); )
662     out.textElement("default", iter.next().toString());
663
664     out.endElement();
665 }
666
667 public String JavaDoc toString() {
668     StringWriter JavaDoc sw = new StringWriter JavaDoc();
669     writeXML(new XMLWriter(sw));
670     return sw.toString();
671 }
672
673 }
674
Popular Tags