1 /*
2 Copyright (C) 2000 - 2007 Grid Systems, S.A.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 /*
19 * Project: KernelConfigurator
20 * Created on 03-mar-2004
21 *
22 * Copyright (c)2003 Grid Systems
23 */
24 package com.gridsystems.config.tools.console;
25
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.ResourceBundle;
31 import java.util.MissingResourceException;
32 import java.text.MessageFormat;
33
34 import com.gridsystems.config.Configurator;
35 import com.gridsystems.config.ConsoleConfiguratorView;
36 import com.gridsystems.config.ErrorListener;
37 import com.gridsystems.config.app.UI;
38
39 /**
40 * Base implementation for console views.
41 *
42 * @author <a href="mailto:rruiz@gridsystems.com">Rodrigo Ruiz Aguayo</a>
43 * @version 1.0
44 */
45 public abstract class AbstractConsoleView implements ConsoleConfiguratorView,
46 ErrorListener {
47 /**
48 * Default console width.
49 */
50 private static final int DEFAULT_CONSOLE_WIDTH = 70;
51
52 /**
53 * Resource bundle to use for message I18N.
54 */
55 protected ResourceBundle bundle = null;
56
57 /**
58 * Width of the console view.
59 */
60 protected int width;
61
62 /**
63 * Form fields.
64 */
65 private Object[] form = null;
66
67 /**
68 * Registered key mappings.
69 */
70 private Map<String, ConsoleViewAction> keyMappings
71 = new HashMap<String, ConsoleViewAction>();
72
73 /**
74 * Message to show at the bottom line of the view.
75 */
76 private MessageField messages = new MessageField();
77
78 /**
79 * The configurator this view is associated to.
80 */
81 private Configurator config;
82
83 /**
84 * Creates a new instance with default with and the specified Configuration instance.
85 *
86 * @param config The configuration instance
87 */
88 public AbstractConsoleView(Configurator config) {
89 this(config, DEFAULT_CONSOLE_WIDTH);
90 }
91
92 /**
93 * Creates a new instance.
94 *
95 * @param config The configuration to use
96 * @param width The view width
97 */
98 public AbstractConsoleView(Configurator config, int width) {
99 this.bundle = config.getBundle();
100 this.width = width;
101 this.config = config;
102 this.config.setErrorListener(this);
103
104 setApplyDiscardMappings(config);
105 }
106
107 /**
108 * Registers the default apply and discard actions linked to the specified
109 * configuration instance.
110 *
111 * @param config The configuration instance which actions will affect to
112 */
113 public void setApplyDiscardMappings(Configurator config) {
114 addKeyMapping(new ApplyAction(config));
115 addKeyMapping(new DiscardAction(config));
116 }
117
118 /**
119 * Gets the configurator this view is associated to.
120 *
121 * @return The configurator
122 */
123 public Configurator getConfigurator() {
124 return config;
125 }
126
127 /**
128 * @see com.gridsystems.config.ConsoleConfiguratorView#execute()
129 */
130 public void execute() {
131 Object[] localForm = buildForm();
132 String sPrompt = UI.getString("console.menu.prompt");
133
134 boolean alive = true;
135 while (alive) {
136 try {
137 paintForm(localForm);
138 System.out.println();
139 String line = ConsoleTools.readLine(sPrompt).toUpperCase();
140 ConsoleViewAction action = (ConsoleViewAction)keyMappings.get(line);
141 if (action != null) {
142 alive = action.execute(this);
143 }
144 } catch (Exception e) {
145 e.printStackTrace();
146 }
147 }
148 }
149
150 /**
151 * Displays the "view box".
152 *
153 * @param form The contents of the box
154 */
155 protected void paintForm(Object[] form) {
156 ConsoleTools.clear();
157 ConsoleTools.paintBox(getTitle(), form, width);
158
159 // Resets the error line
160 messages.reset();
161 }
162
163 /**
164 * Creates the contents of the "view box". It adds an Apply/Discard button bar
165 * to the bottom of the box after calling to {@link #getContents(java.util.List)}
166 *
167 * @return An array of objects conforming the "view box" contents, that can be
168 * directly used as an argument by the painBox method in ConsoleTools.
169 */
170 protected Object[] buildForm() {
171 if (form == null) {
172 String sApplyText = UI.getString("console.apply.text");
173 String sDiscardText = UI.getString("console.discard.text");
174
175 ArrayList<Object> items = new ArrayList<Object>();
176 getContents(items);
177 items.add("-");
178 items.add(" [" + sApplyText + "] [" + sDiscardText + "]");
179
180 // Place holders for error messages
181 items.add(null);
182 items.add(messages);
183
184 form = new Object[items.size()];
185 items.toArray(form);
186 }
187 return form;
188 }
189
190 /**
191 * Registers a string with a ConsoleViewAction instance. The action will
192 * be executed when the user types the key string.
193 *
194 * @param key The associated command string
195 * @param action The action to execute when key is typed
196 */
197 public void addKeyMapping(String key, ConsoleViewAction action) {
198 keyMappings.put(key.toUpperCase(), action);
199 }
200
201 /**
202 * Registers action with its own key mapping.
203 *
204 * @param action The action to execute
205 */
206 public void addKeyMapping(ConsoleViewAction action) {
207 addKeyMapping(action.getKeyMapping(), action);
208 }
209
210 /**
211 * Registers the keymappings of all actions in the specified group field.
212 *
213 * @param group The group field.
214 */
215 public void addKeyMapping(GroupField group) {
216 ConsoleViewAction[] actions = group.getActions();
217 for (int i = 0; i < actions.length; i++) {
218 addKeyMapping(actions[i]);
219 }
220 }
221
222 /**
223 * Removes the key mapping for the specified action.
224 *
225 * @param action the action to remove
226 */
227 public void removeKeyMapping(ConsoleViewAction action) {
228 keyMappings.remove(action.getKeyMapping().toUpperCase());
229 }
230
231 /**
232 * Removes all key mappings in the specified group field.
233 *
234 * @param group The group field
235 */
236 public void removeKeyMapping(GroupField group) {
237 ConsoleViewAction[] actions = group.getActions();
238 for (int i = 0; i < actions.length; i++) {
239 removeKeyMapping(actions[i]);
240 }
241 }
242
243 /**
244 * Gets an array with the items conforming the contents of the "view box".
245 *
246 * @param list A list where to put the contents of the box
247 */
248 protected abstract void getContents(List<Object> list);
249
250 /**
251 * Creates a ValueField that will share the same bundle as this instance.
252 *
253 * @param linePattern The line pattern of the new field
254 * @param valuePattern The value pattern of the new field
255 * @return The field
256 */
257 protected ValueField createValueField(String linePattern, String valuePattern) {
258 return new ValueField(bundle, linePattern, valuePattern);
259 }
260
261 /**
262 * Creates a SelectionField that will share the same bundle as this instance.
263 *
264 * @param linePattern a String with the line pattern of the new field.
265 * @param valuePattern a String with the value pattern of the new field.
266 * @param values a String[] with the list of valid values of the new field.
267 *
268 * @return the SelectionField created.
269 */
270 protected SelectionField createSelectionField(
271 String linePattern, String valuePattern, String[] values) {
272 return new SelectionField(bundle, linePattern, valuePattern, values);
273 }
274
275 /**
276 * Creates a BoolField that will share the same bundle as this instance.
277 *
278 * @param linePattern The field line pattern
279 * @return The field
280 */
281 protected BoolField createBoolField(String linePattern) {
282 return new BoolField(bundle, linePattern);
283 }
284
285 /**
286 * Creates a password field that will share the same bundle as this instance.
287 *
288 * @param linePattern The field line pattern
289 * @return The field
290 */
291 protected PasswordField createPasswordField(String linePattern) {
292 return new PasswordField(bundle, linePattern);
293 }
294
295 /**
296 * Creates a password field that will share the same bundle as this instance.
297 *
298 * @param linePattern The field line pattern
299 * @param value the value
300 * @return The field
301 */
302 protected ConstantField createConstantField(String linePattern, String value) {
303 return new ConstantField(bundle, linePattern, value);
304 }
305 /**
306 * Gets a localized string, using the found string as a pattern to be used
307 * by a MessageFormat instance.
308 *
309 * @param key The key for the localized pattern
310 * @param params The parameters for pattern substitution
311 * @return The localized / formatter string
312 * @throws NullPointerException if the bundle is not set
313 */
314 public String getString(String key, Object[] params) {
315 if (bundle == null) {
316 throw new NullPointerException("Bundle not set");
317 }
318
319 try {
320 String pattern = bundle.getString(key);
321 return MessageFormat.format(pattern, params);
322 } catch (MissingResourceException e) {
323 return key + "#not found";
324 }
325 }
326
327 /**
328 * Gets a localized string, using the found string as a pattern to be used
329 * by a MessageFormat instance.
330 *
331 * @param key The key for the localized pattern
332 * @return The localized / formatter string
333 * @throws NullPointerException if the bundle is not set
334 */
335 public String getString(String key) {
336 return getString(key, null);
337 }
338
339 /**
340 * Shows an error message in the bottom line of the view.
341 * <p>
342 * The message is removed on the next view refresh
343 *
344 * @param message The error message lines
345 */
346 public void showError(String[] message) {
347 int count = (message == null) ? 0 : message.length;
348 if (count == 0) {
349 // No message to show
350 messages.reset();
351 } else {
352 String title = UI.getString("messages.error");
353 StringBuffer sb = new StringBuffer();
354
355 messages.addLine("-");
356 for (int i = 0; i < count; i++) {
357 sb.setLength(0);
358 if (i == 0) {
359 sb.append(title).append(": ");
360 } else {
361 ConsoleTools.fill(sb, ' ', title.length() + 2);
362 }
363 sb.append(message[i]);
364 messages.addLine(sb.toString());
365 }
366 }
367 }
368
369 /**
370 * Map of current errors in the view.
371 */
372 Map<String, String> errors = new HashMap<String, String>();
373
374 /**
375 * {@inheritDoc}
376 */
377 public void updateError(String id, String msg, boolean add) {
378 if (add) {
379 System.err.println("ERROR: " + msg);
380 errors.put(id, msg);
381 } else {
382 errors.remove(id);
383 }
384 }
385
386 /**
387 * {@inheritDoc}
388 */
389 public void clearErrors() {
390 errors.clear();
391 }
392
393 /**
394 * {@inheritDoc}
395 */
396 public boolean hasErrors() {
397 return this.errors.size() == 0;
398 }
399 }