View Javadoc

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  	// the map should be ordered...
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 			// check prefixes
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(); // don't print table when no parameters are
158 		// defined
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 		// headers
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 		// underline row
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 		// convert to String[][]
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 		// set column widths to maximum width of data in that column plus margin
265 		// of 1.
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 		// write table data
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 				// pad with spaces
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 		// check all required parameters have values (if requested)
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) { // might be help request
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) { // program help
338 					outStream.println(info());
339 				} else { // help for a type
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(";"); // mark end of ParamSet
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 }