1 package net.sourceforge.jparam.paramset;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.InputStream;
6 import java.io.PrintStream;
7 import java.util.Collection;
8 import java.util.HashMap;
9 import java.util.Iterator;
10 import java.util.LinkedList;
11 import java.util.List;
12 import java.util.Map;
13
14 import net.sourceforge.jparam.JParam;
15 import net.sourceforge.jparam.JParamException;
16 import net.sourceforge.jparam.parser.AssignmentListener;
17 import net.sourceforge.jparam.parser.TreeNode;
18 import net.sourceforge.jparam.util.Utils;
19
20 /***
21 * This class helps in parsing and handling user input from the command line,
22 * you can define sets of parameters, give them types and binding information
23 * and use this to read the entire input as a whole
24 *
25 * @author ron_sidi
26 *
27 */
28 public class ParamSet implements AssignmentListener {
29 private PrintStream outStream = System.out;
30 private PrintStream feedbackStream = System.err;
31
32 private int m_max_feedback_length = 100;
33
34
35 private Map param_map = new HashMap();
36
37 private List param_names = new LinkedList();
38
39 private String description = "";
40
41 private boolean m_warning_is_error = true;
42
43 private boolean m_ignore_unexpected_param = false;
44
45 private boolean m_feedback_enabled = false;
46
47 /***
48 * Create an empty ParamSet object.
49 */
50 public ParamSet() {
51 }
52
53 /***
54 * Create a ParamSet object with the given general description, and no
55 * parameters.
56 */
57 public ParamSet(String description) {
58 setDescription(description);
59 }
60
61 public void clearValues() {
62 for (Iterator iter = param_map.values().iterator(); iter.hasNext();) {
63 Param param = (Param) iter.next();
64 param.clearValue();
65 }
66 }
67
68 /***
69 * Sets the ParamSet's general description
70 */
71 public void setDescription(String description) {
72 this.description = description;
73 }
74
75 /***
76 * Returns the ParamSet's general description
77 */
78 public String getDescription() {
79 return description;
80 }
81
82 /***
83 * Adds the given parameter to the set. Throws an exception if a parameter
84 * by the same name already exists in the set.
85 */
86 public void addParam(Param param) throws JParamException {
87 String name = param.getName();
88 if (param_map.get(name) != null) {
89 throw new JParamException("Duplicate definition of parameter \'"
90 + name + "\'.");
91 }
92 param_map.put(name, param);
93 param_names.add(name);
94 }
95
96 /***
97 * Returns the parameter with the given name, or null if no such parameter
98 * exists.
99 */
100 public Param getParam(String name) throws MatchException {
101 Object param = param_map.get(name);
102 if (param == null) {
103
104 List possibleNames = new LinkedList();
105 for (Iterator iter = param_names.iterator(); iter.hasNext();) {
106 String paramName = (String) iter.next();
107 if (paramName.startsWith(name)) {
108 possibleNames.add(paramName);
109 param = param_map.get(paramName);
110 }
111 }
112
113 if (param == null) {
114 throw new MatchException(false, "No match for parameter '"
115 + name + "'.");
116 }
117 if (possibleNames.size() > 1) {
118 throw new MatchException(true,
119 "Reference to parameter aval is ambiguous. Candidates are: "
120 + arraytoString(possibleNames) + ".");
121 }
122 }
123 return (Param) param;
124 }
125
126 private String arraytoString(List objects) {
127 StringBuffer buf = new StringBuffer();
128 for (Iterator iter = objects.iterator(); iter.hasNext();) {
129 buf.append(iter.next());
130 if (iter.hasNext()) {
131 buf.append(", ");
132 }
133 }
134 return buf.toString();
135 }
136
137 /***
138 * Returns all parameters
139 */
140 public Collection getParams() {
141 List params = new LinkedList();
142 for (Iterator iter = param_names.iterator(); iter.hasNext();) {
143 String paramName = (String) iter.next();
144 params.add(param_map.get(paramName));
145 }
146 return params;
147 }
148
149 /***
150 * Returns a human readable description of the ParamSet, including all
151 * parameters
152 */
153 public String info() {
154 StringBuffer str = new StringBuffer(description);
155 str.append('\n');
156 if (param_map.isEmpty())
157 return str.toString();
158
159
160 str
161 .append("Usage: progname <param1>=<val1> <param2>=<val2> -<flag1> -no_<flag2>...\n");
162 str.append("Use exact parameter names.\n");
163 str
164 .append("Multiple assignment to the same parameter is not allowed.\n");
165 str.append("\n");
166
167 String table = formatTable(getParamsTable());
168 str.append(table);
169
170 return str.toString();
171 }
172
173 private String[][] getParamsTable() {
174
175 List rows = new LinkedList();
176 List row;
177
178
179 row = new LinkedList();
180 row.add("Name");
181 row.add("Type");
182 row.add("I/O");
183 row.add("Default Value");
184 row.add("Description");
185 rows.add(row);
186
187
188 row = new LinkedList();
189 row.add("====");
190 row.add("====");
191 row.add("===");
192 row.add("=============");
193 row.add("===========");
194 rows.add(row);
195
196 Iterator i = getParams().iterator();
197 while (i.hasNext()) {
198 Param param = (Param) i.next();
199
200 row = new LinkedList();
201 row.add(param.getName());
202 row.add(param.getTypeBound().getName());
203
204 if (param.isInput() && !param.isOutput())
205 row.add(" I ");
206 if (!param.isInput() && param.isOutput())
207 row.add(" O ");
208 if (param.isInput() && param.isOutput())
209 row.add("I/O");
210
211 if (param.isInput()) {
212 Object defaultValue = param.getDefaultValue();
213 if (defaultValue != null) {
214 String defaultString;
215 try {
216 defaultString = JParam.toString(defaultValue);
217 } catch (JParamException ex) {
218 defaultString = "Output Error";
219 }
220 row.add(defaultString);
221 } else {
222 row.add("[ required ]");
223 }
224 } else {
225 row.add("");
226 }
227
228 row.add(param.getDescription());
229
230 rows.add(row);
231 }
232
233
234 int nRows = rows.size();
235 int nCols = ((List) rows.get(0)).size();
236 String[][] table = new String[nRows][nCols];
237 int iRow = 0;
238 for (Iterator curRow = rows.iterator(); curRow.hasNext();) {
239 List curCol = (List) curRow.next();
240 Utils.assert(row.size() == nCols, "row.size() == nCols");
241 int iCol = 0;
242 for (Iterator curCell = curCol.iterator(); curCell.hasNext();) {
243 table[iRow][iCol] = (String) curCell.next();
244 iCol++;
245 }
246 iRow++;
247 }
248 return table;
249 }
250
251 private static String formatTable(String[][] table) {
252 int iCol, nCols;
253 int iRow, nRows;
254
255 nRows = table.length;
256 Utils.assert(nRows != 0, "Table is empty");
257 nCols = table[0].length;
258
259 for (iRow = 0; iRow < nRows; ++iRow) {
260 Utils.assert(table[iRow].length == nCols,
261 "Not all rows have same number of columns");
262 }
263
264
265
266 int[] columnWidth = new int[nCols];
267 for (iCol = 0; iCol < nCols; ++iCol) {
268 int max = 0;
269 for (iRow = 0; iRow < nRows; ++iRow) {
270 int size = table[iRow][iCol].length();
271 if (size > max)
272 max = size;
273 }
274 columnWidth[iCol] = max + 1;
275 }
276
277
278 StringBuffer str = new StringBuffer();
279 for (iRow = 0; iRow < nRows; ++iRow) {
280 for (iCol = 0; iCol < nCols; ++iCol) {
281 String cellData = table[iRow][iCol];
282 str.append(table[iRow][iCol]);
283
284
285 int i;
286 for (i = cellData.length(); i < columnWidth[iCol]; ++i) {
287 str.append(' ');
288 }
289 }
290 str.append('\n');
291 }
292
293 return str.toString();
294 }
295
296 /***
297 * Reads values for all input parameter from given InputStream. Checks that
298 * all parameters have values.
299 */
300 public void input(InputStream in) throws JParamException {
301 input(in, true);
302 }
303
304 /***
305 * Reads values for all input parameter from given InputStream. If check is
306 * true, checks that all parameters have values.
307 */
308 public void input(InputStream in, boolean check) throws JParamException {
309 JParam.readValueSet(in, this);
310
311 if (m_feedback_enabled) {
312 write_feedback();
313 }
314
315 if (check)
316 this.check();
317 }
318
319 /***
320 * Reads values for all input parameter from 'args'. Checks that all
321 * parameters have values.
322 */
323 public void input(String[] args) throws JParamException {
324 input(args, true);
325 }
326
327 /***
328 * Reads values for all input parameter from 'args'. If check is true,
329 * checks that all parameters have values.
330 */
331 public void input(String[] args, boolean check) throws JParamException {
332 if (args.length == 1 || args.length == 2) {
333 if (args[0].equals("!") || args[0].equals("/?")
334 || args[0].equals("-?") || args[0].equals("--help")
335 || args[0].equals("/h") || args[0].equals("/H")
336 || args[0].equals("/help")) {
337 if (args.length == 1) {
338 outStream.println(info());
339 } else {
340 String topic = args[1];
341 outStream.println(JParam.help(topic));
342 }
343 System.exit(0);
344 }
345 }
346
347 StringBuffer strbuf = new StringBuffer();
348 for (int i = 0; i < args.length; ++i) {
349 strbuf.append('\1');
350 strbuf.append(args[i]);
351 }
352
353 InputStream in = new ByteArrayInputStream(strbuf.toString().getBytes());
354 input(in, check);
355 }
356
357 /***
358 * Checks that all parameters have values. Throws an exception if check
359 * fails.
360 */
361 public void check() throws JParamException {
362
363 Iterator i = getParams().iterator();
364 while (i.hasNext()) {
365 Param param = (Param) i.next();
366 if (param.isInput() && (param.getDefaultValue() == null)
367 && !param.wasAssignedTo()) {
368 throw new JParamException(
369 "No value given for required parameter "
370 + param.getName() + ".");
371 }
372 }
373 }
374
375 /***
376 * Writes all output parameters to outStream
377 */
378 public void output() throws JParamException {
379 output(outStream);
380 }
381
382 /***
383 * Writes all output parameters to given PrintStream.
384 */
385 public void output(PrintStream out) throws JParamException {
386 Iterator i = getParams().iterator();
387 while (i.hasNext()) {
388 Param param = (Param) i.next();
389 if (param.isOutput()) {
390 Object val = param.getValue();
391 out.print(param.getName());
392 out.print(" = ");
393 JParam.writeObject(val, out);
394 out.print('\n');
395 }
396 }
397 out.println(";");
398 }
399
400 public void write_feedback() {
401 feedbackStream
402 .println("--------------------------------------------------");
403 feedbackStream.println("Values of input parameters:");
404
405 Iterator i = getParams().iterator();
406 while (i.hasNext()) {
407 Param param = (Param) i.next();
408 if (param.isInput()) {
409 Object val = param.getValue();
410 feedbackStream.print(param.getName());
411 if (param.hasGivenValue()) {
412 if (!param.wasAssignedTo()) {
413 feedbackStream.print(" [default]");
414 }
415 feedbackStream.print(" = ");
416 ByteArrayOutputStream argBytesStream = new ByteArrayOutputStream();
417 PrintStream argsStream = new PrintStream(argBytesStream);
418
419 try {
420 JParam.writeObject(val, argsStream);
421 } catch (JParamException e) {
422 e.printStackTrace();
423 }
424 String argValue = argBytesStream.toString();
425
426 if (m_max_feedback_length > 0) {
427 if (argValue.length() > m_max_feedback_length) {
428 argValue = argValue.substring(0,
429 m_max_feedback_length)
430 + " ...";
431 }
432 }
433
434 feedbackStream.println(argValue);
435 } else {
436 feedbackStream.println(" [unassigned]");
437 }
438 }
439 }
440 feedbackStream
441 .println("--------------------------------------------------");
442 feedbackStream.println();
443 }
444
445 /***
446 * Called by parser for each assignemnt to value set.
447 */
448 public void notifyAssignment(String name, TreeNode value)
449 throws JParamException {
450 Param p;
451 try {
452 p = getParam(name);
453 } catch (MatchException e) {
454 if (!e.isAmbigous() && m_ignore_unexpected_param) {
455 return;
456 }
457
458 if (m_warning_is_error) {
459 throw e;
460 }
461
462 outStream.println("WARNING: Assignment to parameter " + name
463 + " was ignored. " + e.getMessage());
464 return;
465 }
466
467 if (p == null)
468 throw new JParamException("No match for parameter \'" + name
469 + "\'.");
470
471 if (!p.isInput())
472 throw new JParamException("Parameter " + name
473 + " isn't an input parameter.");
474
475 if (p.wasAssignedTo())
476 throw new JParamException("Multiple assignment to parameter "
477 + name + ".");
478
479 Object constructedValue = value.getConstructedValue(p.getTypeBound());
480 p.setValue(constructedValue);
481 }
482
483 public void setOutput(PrintStream outStream) {
484 this.outStream = outStream;
485 }
486
487 public void warning_is_error(boolean b) {
488 m_warning_is_error = b;
489 }
490
491 public void ignore_unexpected(boolean b) {
492 m_ignore_unexpected_param = b;
493 }
494
495 public void feedback_enabled(boolean b) {
496 m_feedback_enabled = b;
497 }
498
499 public void max_feedback_length(int i) {
500 m_max_feedback_length = i;
501 }
502
503 public void set_feedback_stream(PrintStream ps) {
504 feedbackStream = ps;
505 }
506
507 }