001: package org.obe.runtime.tool;
002:
003: import com.toedter.calendar.JCalendar;
004: import org.obe.client.api.ClientConfig;
005: import org.obe.client.api.repository.UpdateProcessAttributesMetaData;
006: import org.obe.client.api.tool.Parameter;
007: import org.obe.client.api.tool.ToolInvocation;
008: import org.obe.util.DateUtilities;
009: import org.obe.util.SpringUtilities;
010: import org.obe.xpdl.model.data.Type;
011:
012: import javax.swing.*;
013: import javax.swing.text.JTextComponent;
014: import java.awt.*;
015: import java.awt.event.*;
016: import java.io.*;
017: import java.lang.reflect.InvocationTargetException;
018: import java.text.NumberFormat;
019: import java.util.Date;
020:
021: /**
022: * A tool agent that updates a set of process instance attributes.
023: *
024: * @author Adrian Price
025: */
026: public class UpdateProcessAttributes extends AbstractToolAgent {
027: private static final long serialVersionUID = 5475443176807253400L;
028: private static final boolean USE_STDIO = ClientConfig.useSTDIO();
029: private static final Dimension BUTTON_SIZE = new Dimension(75, 25);
030: private static final Dimension COMPONENT_SIZE = new Dimension(200,
031: 20);
032: private static final int GAP = 10;
033:
034: private final UpdateProcessAttributesMetaData _metadata;
035: private static final int BUFFER_SIZE = 1024;
036: protected static final String STATUS = "status";
037:
038: private static final class JFileSelector extends JPanel {
039: private JTextField _name;
040:
041: {
042: setLayout(new FlowLayout());
043: _name = new JTextField();
044: _name.setPreferredSize(COMPONENT_SIZE);
045: add(_name);
046: JButton browse = new JButton("Browse...");
047: browse.addActionListener(new ActionListener() {
048: public void actionPerformed(ActionEvent ae) {
049: JFileChooser chooser = new JFileChooser(getFile());
050: int ret = chooser
051: .showOpenDialog(JFileSelector.this );
052: if (ret == JFileChooser.APPROVE_OPTION) {
053: try {
054: setText(chooser.getSelectedFile()
055: .getCanonicalPath());
056: } catch (IOException e) {
057: // Nothing to do if we can't access the file.
058: }
059: }
060: }
061: });
062: add(browse);
063: }
064:
065: JFileSelector() {
066: }
067:
068: JFileSelector(String filename) {
069: setText(filename);
070: }
071:
072: String getText() {
073: return _name.getText();
074: }
075:
076: void setText(String t) {
077: _name.setText(t);
078: }
079:
080: File getFile() {
081: String text = getText();
082: File location = text == null ? null : new File(text);
083: return location == null || !location.exists() ? null
084: : location;
085: }
086: }
087:
088: private class UpdateProcessAttributesDialog extends JDialog {
089: private ToolInvocation _ti;
090: private int _exitCode = EXIT_CANCEL;
091: private static final int MAX_DIGITS_INTEGER = 19;
092:
093: UpdateProcessAttributesDialog(ToolInvocation ti) {
094: super ((Frame) null, true);
095: _ti = ti;
096: init();
097: }
098:
099: public int getExitCode() {
100: return _exitCode;
101: }
102:
103: private void init() {
104: if (_metadata.getTitle() != null)
105: setTitle(_metadata.getTitle());
106: setSize(_metadata.getWidth(), _metadata.getHeight());
107:
108: Container contentPane = getContentPane();
109: contentPane.setLayout(new BorderLayout());
110:
111: // Create the component pane.
112: final JComponent componentPane = new JPanel(
113: new SpringLayout());
114:
115: if (_metadata.getStatus()) {
116: // TODO: set up status bar.
117: JLabel statusBar = new JLabel();
118: statusBar.setName(STATUS);
119: contentPane.add(statusBar);
120: }
121: if (_metadata.getScrollbars()) {
122: // TODO: set up scroll bars.
123: }
124:
125: // Create the entry fields in the component pane.
126: final int n = _ti.parameters.length;
127: for (int i = 0; i < n; i++) {
128: Parameter param = _ti.parameters[i];
129: Object value = param.getValue();
130: String strValue = value == null ? null : value
131: .toString();
132:
133: JLabel label = new JLabel(param.getName());
134:
135: JComponent component;
136: JFormattedTextField textField;
137: NumberFormat fmt;
138: Type type = param.getDataType().getType()
139: .getImpliedType();
140: switch (type.value()) {
141: case Type.STRING_TYPE:
142: component = new JTextField(strValue);
143: component.setPreferredSize(COMPONENT_SIZE);
144: break;
145: case Type.FLOAT_TYPE:
146: fmt = NumberFormat.getNumberInstance();
147: fmt.setMaximumFractionDigits(10);
148: fmt.setMaximumFractionDigits(10);
149: component = textField = new JFormattedTextField(fmt);
150: component.setPreferredSize(COMPONENT_SIZE);
151: textField.setText(strValue);
152: break;
153: case Type.INTEGER_TYPE:
154: fmt = NumberFormat.getIntegerInstance();
155: fmt.setMaximumIntegerDigits(MAX_DIGITS_INTEGER);
156: component = textField = new JFormattedTextField(fmt);
157: component.setPreferredSize(COMPONENT_SIZE);
158: textField.setText(strValue);
159: break;
160: case Type.DATETIME_TYPE:
161: component = new JCalendar((Date) value);
162: break;
163: case Type.BOOLEAN_TYPE:
164: component = new JCheckBox((String) null,
165: Boolean.TRUE.equals(value));
166: break;
167: case Type.PERFORMER_TYPE:
168: JComboBox combo = new JComboBox(
169: new Object[] { strValue });
170: combo.setEditable(true);
171: component = combo;
172: component.setPreferredSize(COMPONENT_SIZE);
173: break;
174: case Type.SCHEMA_TYPE:
175: component = new JFileSelector(strValue);
176: break;
177: default:
178: throw new IllegalArgumentException(
179: "Unsupported data type: " + type);
180: }
181: label.setLabelFor(component);
182:
183: componentPane.add(label);
184: componentPane.add(component);
185: }
186: SpringUtilities.makeCompactGrid(componentPane,
187: _ti.parameters.length, 2, GAP, GAP, GAP, GAP >> 1);
188:
189: // Create the buttons pane.
190: JComponent buttonPane = new JPanel(new FlowLayout());
191: JButton ok = new JButton("OK");
192: ok.setPreferredSize(BUTTON_SIZE);
193: ok.setDefaultCapable(true);
194: buttonPane.add(ok);
195:
196: JButton cancel = new JButton("Cancel");
197: cancel.setPreferredSize(BUTTON_SIZE);
198: buttonPane.add(cancel);
199:
200: contentPane.add(componentPane, BorderLayout.CENTER);
201: contentPane.add(buttonPane, BorderLayout.SOUTH);
202:
203: // Layout the dialog and set the default button.
204: getRootPane().setDefaultButton(ok);
205: pack();
206:
207: ok.addActionListener(new ActionListener() {
208: public void actionPerformed(ActionEvent e) {
209: try {
210: Component[] components = componentPane
211: .getComponents();
212: for (int i = 0; i < n; i++) {
213: Component component = components[2 * i + 1];
214: Parameter param = _ti.parameters[i];
215: Object value;
216: int type = param.getDataType().getType()
217: .getImpliedType().value();
218: switch (type) {
219: case Type.STRING_TYPE:
220: value = ((JTextComponent) component)
221: .getText();
222: break;
223: case Type.FLOAT_TYPE:
224: case Type.INTEGER_TYPE:
225: value = ((JFormattedTextField) component)
226: .getValue();
227: break;
228: case Type.DATETIME_TYPE:
229: value = ((JCalendar) component)
230: .getDate();
231: break;
232: case Type.BOOLEAN_TYPE:
233: value = Boolean
234: .valueOf(((AbstractButton) component)
235: .isSelected());
236: break;
237: case Type.PERFORMER_TYPE:
238: value = ((JComboBox) component)
239: .getSelectedItem();
240: break;
241: case Type.SCHEMA_TYPE:
242: File file = ((JFileSelector) component)
243: .getFile();
244: value = readFile(file);
245: break;
246: default:
247: value = null;
248: }
249: param.setValue(value);
250: }
251: } catch (Exception ex) {
252: JOptionPane.showMessageDialog(
253: UpdateProcessAttributesDialog.this ,
254: "An exception occurred: "
255: + ex.getClass().getName()
256: + ": " + ex.getMessage(),
257: "Error", JOptionPane.ERROR_MESSAGE);
258: }
259: _exitCode = EXIT_NORMAL;
260: dispose();
261: }
262:
263: });
264:
265: cancel.addActionListener(new ActionListener() {
266: public void actionPerformed(ActionEvent e) {
267: dispose();
268: }
269: });
270:
271: addWindowListener(new WindowAdapter() {
272: public void windowClosing(WindowEvent e) {
273: dispose();
274: }
275: });
276:
277: addKeyListener(new KeyAdapter() {
278: public void keyPressed(KeyEvent e) {
279: if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
280: dispose();
281: }
282: });
283: }
284: }
285:
286: private static byte[] readFile(File file) throws IOException {
287: byte[] content = null;
288: if (file != null) {
289: InputStream in = null;
290: try {
291: ByteArrayOutputStream out = new ByteArrayOutputStream();
292: in = new FileInputStream(file);
293: byte[] buf = new byte[BUFFER_SIZE];
294: int n;
295: while ((n = in.read(buf)) > 0)
296: out.write(buf, 0, n);
297: content = out.toByteArray();
298: } finally {
299: if (in != null)
300: try {
301: in.close();
302: } catch (IOException e) {
303: // Ignore exceptions on close.
304: }
305: }
306: }
307: return content;
308: }
309:
310: private static void clearSchemaTypeValues(Parameter[] parms) {
311: // Clear values for schema-type attributes that don't map to other
312: // types, because such variables are only updatable via file upload.
313: for (int i = 0; i < parms.length; i++) {
314: Parameter parm = parms[i];
315: if (parm.getDataType().getType().getImpliedType().value() == Type.SCHEMA_TYPE) {
316:
317: parm.setValue(null);
318: }
319: }
320: }
321:
322: private static void write(Writer writer, String key, int value)
323: throws IOException {
324:
325: writer.write(key);
326: writer.write('=');
327: writer.write(String.valueOf(value));
328: writer.write(',');
329: }
330:
331: private static void write(Writer writer, String key, boolean value)
332: throws IOException {
333:
334: writer.write(key);
335: writer.write('=');
336: writer.write(value ? "yes" : "no");
337: writer.write(',');
338: }
339:
340: public UpdateProcessAttributes(
341: UpdateProcessAttributesMetaData metadata) {
342: _metadata = metadata;
343: }
344:
345: public void renderInvocationScript(ToolInvocation ti, Writer writer)
346: throws IOException {
347:
348: clearSchemaTypeValues(ti.parameters);
349:
350: int n = ti.parameters.length;
351: if (n > 0) {
352: writer.write("window.open(\"updateProcessAttributes.jsf");
353: writer.write("?updProcAttrs:procInstId=");
354: writer.write(ti.procInstId);
355: writer.write("&updProcAttrs:workItemId=");
356: writer.write(ti.workItemId);
357: writer.write("&updProcAttrs:toolIndex=");
358: writer.write(String.valueOf(ti.toolIndex));
359: writer.write("\", \"");
360: if (_metadata.getTitle() != null)
361: writer.write(_metadata.getTitle());
362: writer.write("\", \"");
363: write(writer, "height", _metadata.getHeight());
364: write(writer, "width", _metadata.getWidth());
365: write(writer, STATUS, _metadata.getStatus());
366: write(writer, "toolbar", _metadata.getToolbar());
367: write(writer, "menubar", _metadata.getMenubar());
368: write(writer, "location", _metadata.getLocation());
369: write(writer, "scrollbars", _metadata.getScrollbars());
370: writer.write("\");");
371: }
372: }
373:
374: protected int _invokeApplication(ToolInvocation ti)
375: throws InterruptedException, InvocationTargetException {
376:
377: clearSchemaTypeValues(ti.parameters);
378:
379: return USE_STDIO ? invokeViaSTDIO(ti) : invokeViaSwing(ti);
380: }
381:
382: private int invokeViaSTDIO(ToolInvocation ti)
383: throws InvocationTargetException {
384:
385: int exitCode = EXIT_CANCEL;
386: int n = ti.parameters.length;
387: if (n > 0) {
388: // Perform this in a synchronized block to prevent contention with
389: // other threads using STDIO.
390: synchronized (System.in) {
391: PrintStream stdout = System.out;
392: BufferedReader stdin = new BufferedReader(
393: new InputStreamReader(System.in));
394: String title = _metadata.getTitle();
395: stdout.println(title != null ? title
396: : "Update Process Attributes");
397: for (int i = 0; i < n; i++) {
398: Parameter param = ti.parameters[i];
399: Type type = param.getDataType().getType()
400: .getImpliedType();
401: boolean isDate = type.value() == Type.DATETIME_TYPE;
402: boolean isXML = type.value() == Type.SCHEMA_TYPE;
403:
404: // Print the prompt string.
405: stdout.print("Enter value for '");
406: stdout.print(param.getName());
407: stdout.print("' (");
408: stdout.print(type);
409: if (isXML)
410: stdout.print(" - specify XML file to upload");
411: if (isDate) {
412: stdout.print(" - ");
413: stdout.print(DateUtilities.DEFAULT_DATE_FORMAT);
414: }
415: stdout.print(')');
416: Object value = param.getValue();
417: if (value != null) {
418: if (isDate) {
419: value = DateUtilities.getInstance().format(
420: (Date) value);
421: }
422: stdout.print(" [");
423: stdout.print(value.toString());
424: stdout.print("]");
425: }
426: stdout.print(": ");
427: stdout.flush();
428:
429: // Read the new value.
430: try {
431: Object newValue = stdin.readLine().trim();
432: boolean empty = newValue.equals("");
433: if (empty)
434: newValue = null;
435: if (isXML) {
436: if (!empty)
437: newValue = readFile(new File(
438: (String) newValue));
439: } else if (empty)
440: newValue = value;
441: param.setValue(newValue);
442: exitCode = EXIT_NORMAL;
443: } catch (IOException e) {
444: stdout.print("An exception occurred: "
445: + e.getClass().getName() + ": "
446: + e.getMessage());
447: throw new InvocationTargetException(e);
448: }
449: }
450: // Notify waiters that we've finished with STDIO.
451: System.in.notify();
452: }
453: }
454: return exitCode;
455: }
456:
457: private int invokeViaSwing(ToolInvocation ti) {
458: UpdateProcessAttributesDialog dlg = new UpdateProcessAttributesDialog(
459: ti);
460: dlg.setVisible(true);
461: return dlg.getExitCode();
462: }
463: }
|