001: package org.osbl.agent.gui;
002:
003: import java.awt.GridBagConstraints;
004: import java.awt.event.ActionEvent;
005: import java.awt.event.ActionListener;
006: import java.sql.Timestamp;
007: import java.util.ArrayList;
008: import java.util.Date;
009: import java.util.HashMap;
010: import java.util.List;
011: import java.util.Map;
012:
013: import org.concern.Controller;
014: import org.concern.ControllerException;
015: import org.concern.ControllerLookup;
016: import org.concern.Log;
017: import org.concern.Subject;
018: import org.concern.UnknownSubjectException;
019: import org.concern.model.Process;
020: import org.concern.model.ProcessChangeEvent;
021: import org.conform.BeanMeta;
022: import org.conform.PropertyMeta;
023: import org.osbl.agent.gui.action.BeanShellActionController;
024: import org.osbl.agent.gui.action.SetCurrentTimeActionController;
025: import org.osbl.agent.gui.action.SetEnumActionController;
026: import org.osbl.agent.gui.action.SetPropertyActionController;
027: import org.osbl.agent.gui.action.SetValueActionController;
028: import org.osbl.agent.gui.condition.BeanShellConditionController;
029: import org.osbl.agent.gui.condition.EnumConditionController;
030: import org.osbl.agent.gui.condition.FieldConditionController;
031: import org.osbl.agent.gui.condition.NumberConditionController;
032: import org.osbl.agent.gui.condition.OgnlConditionController;
033: import org.osbl.agent.gui.condition.PropertyConditionController;
034: import org.osbl.agent.gui.condition.StringConditionController;
035: import org.osbl.agent.gui.condition.SubjectConditionController;
036: import org.osbl.agent.logic.RuntimeContext;
037: import org.osbl.agent.logic.SubjectRuntimeContext;
038: import org.osbl.agent.model.Rule;
039: import org.osbl.agent.model.RuleContext;
040: import org.osbl.agent.model.action.SetCurrentTimeAction;
041: import org.osbl.client.wings.concern.SubjectForm;
042: import org.osbl.client.wings.form.GenericObjectEditor;
043: import org.osbl.client.wings.form.GenericObjectList;
044: import org.osbl.client.wings.form.ObjectForm;
045: import org.osbl.client.wings.shell.Client;
046: import org.wings.SComboBox;
047: import org.wings.SComponent;
048: import org.wings.SConstants;
049: import org.wings.SDimension;
050: import org.wings.SLabel;
051: import org.wings.SListCellRenderer;
052: import org.wings.SOptionPane;
053: import org.wings.SPanel;
054:
055: /**
056: * The Class SubjectDesignContext.
057: *
058: * @author Sebastian Nozzi.
059: */
060: public class SubjectDesignContext extends DesignContext {
061:
062: /** The process combo. */
063: protected SComboBox processComboFilter;
064:
065: /** The process combo specifier. */
066: protected SComboBox processComboSpecifier;
067:
068: /** The bean meta. */
069: private BeanMeta beanMeta;
070:
071: /** The confirm before changing. */
072: private boolean confirmBeforeChanging = true;
073:
074: /**
075: * Instantiates a new subject design context.
076: *
077: * @param objectEditor the object editor
078: */
079: protected SubjectDesignContext(GenericObjectEditor objectEditor) {
080: super (objectEditor);
081: }
082:
083: /**
084: * Populate process combo.
085: *
086: * @param processCombo the process combo
087: * @param addAny the add any
088: */
089: public void populateProcessCombo(SComboBox processCombo,
090: boolean addAny) {
091:
092: if (addAny) {
093: // First item goes for wildcard "all"
094: processCombo.addItem(msg("all"));
095: }
096:
097: // Now add all processes in the list to the combo box, one by one
098: for (Process process : agentSystem.getProcessList()) {
099: processCombo.addItem(process);
100: }
101: }
102:
103: /**
104: * Adds the process combo.
105: *
106: * @param processCombo the process combo
107: * @param target the target
108: */
109: protected void addProcessCombo(SComboBox processCombo, SPanel target) {
110:
111: GridBagConstraints constraints = new GridBagConstraints();
112:
113: // Add the label for the process combo
114: constraints.gridx = 0;
115: constraints.gridy = 0;
116: constraints.gridwidth = 1;
117: target.add(new SLabel(msg("process") + ":"), constraints);
118:
119: // Add the process combo
120: constraints.gridx = 1;
121: constraints.gridy = 0;
122: constraints.gridwidth = constraints.REMAINDER;
123: target.add(processCombo, constraints);
124: processCombo.setHorizontalAlignment(SConstants.LEFT_ALIGN);
125:
126: processCombo.setRenderer(new SListCellRenderer() {
127:
128: SLabel label = new SLabel();
129:
130: public SComponent getListCellRendererComponent(
131: SComponent list, java.lang.Object value,
132: boolean isSelected, int index) {
133:
134: if (value instanceof Process)
135: label.setText(Client.getInstance()
136: .getResourceProvider().getMessage(
137: "process."
138: + ((Process) value)
139: .getName()));
140: else
141: // Most probably, this happens for the special "<all>" (1st) item of the combo
142: label.setText(value.toString());
143:
144: return label;
145: }
146:
147: });
148: }
149:
150: /**
151: * returns selected process or list of all processes.
152: *
153: * @param processCombo the process combo
154: * @param usingAny the using any
155: *
156: * @return the selected process
157: */
158: List<Process> getSelectedProcessList(SComboBox processCombo,
159: boolean usingAny) {
160:
161: // The first item in the process combo is "all" (processes)
162: if (usingAny && processCombo.getSelectedIndex() == 0) {
163: // In that case return all but the first entry
164:
165: List<Process> allProcesses = new ArrayList<Process>();
166:
167: for (int i = 1; i < processCombo.getItemCount(); i++)
168: allProcesses.add((Process) processCombo.getItemAt(i));
169:
170: return allProcesses;
171: // Otherwise return a list with only the selected process
172: } else {
173: List<Process> oneItemList = new ArrayList<Process>();
174: oneItemList.add((Process) processCombo.getSelectedItem());
175: return oneItemList;
176: }
177: }
178:
179: /* (non-Javadoc)
180: * @see org.osbl.agent.gui.DesignContext#recomputeCurrentRules()
181: */
182: public void recomputeCurrentRules() {
183:
184: currentRules.clear();
185:
186: // Get list of selected Process (may be one or many) and
187: // iterate over them/it
188: for (Process proc : getSelectedProcessList(processComboFilter,
189: true)) {
190: // Re-compute rules for each Process.
191: recomputeCurrentRules(proc);
192: }
193: }
194:
195: /**
196: * Recompute current rules.
197: *
198: * @param proc the proc
199: */
200: private void recomputeCurrentRules(Process proc) {
201:
202: List<Rule> rulesToAdd = new ArrayList<Rule>();
203:
204: // Now filter out non-corresponding Rules...
205: for (Rule aRule : agentSystem.getRules()) {
206:
207: if (belongsToDesignContext(aRule)) {
208: String processName = (String) aRule
209: .getMetaInformation().get(
210: RuleContext.PROCESS_NAME_KEY);
211:
212: if (aRule.isPublic() == false
213: && aRule.getCreatorUser().equals(
214: DesignContext.getCurrentUser()) == false)
215: continue;
216:
217: if (processName.equals(proc.getName())) {
218: rulesToAdd.add(aRule);
219: }
220: }
221: }
222:
223: currentRules.addAll(rulesToAdd);
224: }
225:
226: /* (non-Javadoc)
227: * @see org.osbl.agent.gui.DesignContext#addSideFilters(org.osbl.agent.gui.SidePanel)
228: */
229: public void addSideFilters(final SidePanel targetPanel) {
230:
231: // This two combos will let the user specify the process/activity
232: // pair.
233: processComboFilter = new SComboBox();
234:
235: processComboFilter.setPreferredSize(new SDimension("140px",
236: SDimension.AUTO));
237:
238: addProcessCombo(processComboFilter, targetPanel);
239:
240: populateProcessCombo(processComboFilter, true);
241:
242: // Selecting another Process causes Rules to be updated.
243: processComboFilter.addActionListener(new ActionListener() {
244: public void actionPerformed(ActionEvent e) {
245: recomputeCurrentRules();
246: targetPanel.refreshTable();
247: }
248: });
249:
250: }
251:
252: /* (non-Javadoc)
253: * @see org.osbl.agent.gui.DesignContext#addSpecifierComponents(org.osbl.agent.gui.RuleEditorPanel)
254: */
255: public void addSpecifierComponents(RuleEditorPanel targetPanel) {
256: // This two combos will let the user specify the process/activity
257: // pair.
258: processComboSpecifier = new SComboBox();
259:
260: addProcessCombo(processComboSpecifier, targetPanel.contentPane);
261:
262: populateProcessCombo(processComboSpecifier, false);
263:
264: updateBeanMeta();
265:
266: processComboSpecifier
267: .addActionListener(new ProcessChangeListener() {
268: protected void processChanged() {
269: }
270: });
271: }
272:
273: /* (non-Javadoc)
274: * @see org.osbl.agent.gui.DesignContext#enableSpecifierComponents(boolean)
275: */
276: public void enableSpecifierComponents(boolean enabled) {
277: processComboSpecifier.setEnabled(enabled);
278: }
279:
280: /**
281: * The listener interface for receiving processChange events.
282: * The class that is interested in processing a processChange
283: * event implements this interface, and the object created
284: * with that class is registered with a component using the
285: * component's <code>addProcessChangeListener<code> method. When
286: * the processChange event occurs, that object's appropriate
287: * method is invoked.
288: *
289: * @see ProcessChangeEvent
290: */
291: protected abstract class ProcessChangeListener implements
292: ActionListener {
293:
294: /** The old process combo index. */
295: int oldProcessComboIndex;
296:
297: /* (non-Javadoc)
298: * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
299: */
300: public void actionPerformed(ActionEvent e) {
301:
302: if (oldProcessComboIndex == processComboSpecifier
303: .getSelectedIndex())
304: return;
305:
306: if (confirmProcessChange()) {
307: SOptionPane.showYesNoDialog(ruleEditorPanel,
308: msg("processChangeMessage"),
309: msg("processChangeTitle"),
310: new ActionListener() {
311: public void actionPerformed(ActionEvent e) {
312:
313: if (e.getActionCommand().equals(
314: SOptionPane.NO_ACTION)) {
315:
316: // Revert the user change in the
317: // combo-box
318: processComboSpecifier
319: .setSelectedIndex(oldProcessComboIndex);
320:
321: } else {
322: actionOnChange();
323: }
324: }
325: });
326: } else {
327: actionOnChange();
328: }
329: }
330:
331: /**
332: * Action on change.
333: */
334: private void actionOnChange() {
335: updateBeanMeta();
336:
337: ruleEditorPanel.removeAllActionPanels();
338: ruleEditorPanel.removeAllConditionPanels();
339:
340: oldProcessComboIndex = processComboSpecifier
341: .getSelectedIndex();
342:
343: processChanged();
344: }
345:
346: /**
347: * Process changed.
348: */
349: protected abstract void processChanged();
350:
351: }
352:
353: /**
354: * Confirm process change.
355: *
356: * @return true, if successful
357: */
358: protected boolean confirmProcessChange() {
359:
360: // No need to show an alert of actions/conditions getting lost
361: // if there are not any.
362: if (ruleEditorPanel.getActionPanelCount() == 0
363: && ruleEditorPanel.getConditionPanelCount() == 0)
364: return false;
365: else
366: return confirmBeforeChanging;
367: }
368:
369: /**
370: * Sets the confirm process change.
371: *
372: * @param confirmBeforeChanging the new confirm process change
373: */
374: public void setConfirmProcessChange(boolean confirmBeforeChanging) {
375: this .confirmBeforeChanging = confirmBeforeChanging;
376: }
377:
378: /**
379: * Update bean meta.
380: */
381: void updateBeanMeta() {
382: try {
383:
384: beanMeta = Client.getInstance().getBeanMetaProvider()
385: .getBeanMeta(
386: Class.forName(getSpecifiedProcess()
387: .getSubject()));
388: } catch (ClassNotFoundException exception) {
389: // TODO Auto-generated catch block
390: exception.printStackTrace();
391: }
392: }
393:
394: /**
395: * Gets the specified process.
396: *
397: * @return the specified process
398: */
399: Process getSpecifiedProcess() {
400: return (Process) processComboSpecifier.getSelectedItem();
401: }
402:
403: /**
404: * (non-Javadoc).
405: *
406: * @return the controllers
407: *
408: * @see org.osbl.agent.gui.OperationPanel#getControllers()
409: */
410: public List<OperationController> getActionControllers() {
411:
412: // We'll be returning a List of OperationControllers (actually
413: // ActionControllers)
414: List<OperationController> result = new ArrayList<OperationController>();
415:
416: // Now we'll process all properties of the Subject we are working with
417: // and add a suitable ActionController for each of them so that the user
418: // can set some field values.
419:
420: // We get the properties from the beanMeta, which contains (meta)
421: // information
422: // about the subject type we are dealing with. This information can be
423: // very
424: // easily retrieved (easier than using java-reflection).
425: PropertyMeta[] properties = beanMeta.getProperties();
426:
427: // Iterate over all properties of the subject class
428: for (PropertyMeta prop : properties) {
429:
430: SetPropertyActionController newController;
431:
432: if (prop.getType().isEnum()) {
433: newController = new SetEnumActionController();
434: // If the type is one of the supported time classes (see class
435: // documentation)
436: } else if (SetCurrentTimeAction.isSupportedTimeClass(prop
437: .getType()))
438: // The controller will be an automatic time-setter...
439: newController = new SetCurrentTimeActionController();
440: else
441: // ...otherwise it will be a normal value setter (via input
442: // field).
443: newController = new SetValueActionController();
444:
445: // Setting the propertyMeta is vital when later the ActionController
446: // is
447: // asked to generate the corresponding Action
448: newController.setPropertyMeta(prop);
449:
450: // Add our new controller for the property in turn to the list of
451: // choices
452: result.add(newController);
453: }
454:
455: // Add one (and only one) BeanShellActionController to the choices
456: BeanShellActionController beanShellActionController = new BeanShellActionController();
457: result.add(beanShellActionController);
458:
459: // Return our list of ActionControllers
460: return result;
461: }
462:
463: /*
464: * (non-Javadoc)
465: *
466: * @see org.osbl.agent.gui.OperationPanel#getControllers()
467: */
468: public List<OperationController> getConditionControllers() {
469:
470: // We'll be returning a List of OperationControllers (actually
471: // ConditionControllers)
472: List<OperationController> result = new ArrayList<OperationController>();
473:
474: // This first Controller lets you query if a (concern)condition is met
475: // or not
476: SubjectConditionController subjectConditionController = new SubjectConditionController(
477: getSpecifiedProcess());
478: result.add(subjectConditionController);
479:
480: // These Controllers let you test a property of the Subject against some
481: // user
482: // provided value.
483:
484: // Get the properties of the subject and for each of them...
485: for (PropertyMeta propertyMeta : beanMeta.getProperties()) {
486:
487: PropertyConditionController newController = null;
488:
489: // ... according to the type of the property find and instantiate
490: // the corresponding PropertyConditionController...
491:
492: // ... either a special version of it
493: if (propertyMeta.getType().isEnum()) {
494: newController = new EnumConditionController();
495: } else if (Number.class.isAssignableFrom(propertyMeta
496: .getType())) {
497: newController = new NumberConditionController();
498: } else if (String.class.isAssignableFrom(propertyMeta
499: .getType())) {
500: newController = new StringConditionController();
501: } else {
502: // ... or a generic form...
503: newController = new FieldConditionController();
504: }
505:
506: // Setting the propertyMeta is vital when later the ConditionController
507: // is asked to generate the corresponding Condition
508: newController.setPropertyMeta(propertyMeta);
509:
510: // Add our new controller for the property in turn to the list of
511: // choices
512: result.add(newController);
513: }
514:
515: // Add an OgnlConditionController (only one)
516: OgnlConditionController ognlConditionController = new OgnlConditionController();
517: result.add(ognlConditionController);
518:
519: // Add an BeanShellConditionController (only one)
520: BeanShellConditionController beanShellConditionController = new BeanShellConditionController();
521: result.add(beanShellConditionController);
522:
523: return result;
524: }
525:
526: /* (non-Javadoc)
527: * @see org.osbl.agent.gui.DesignContext#populateRule(org.osbl.agent.model.Rule)
528: */
529: public void populateRule(Rule editedRule) {
530:
531: Process process = getSpecifiedProcess();
532:
533: editedRule.getMetaInformation().put(
534: RuleContext.PROCESS_NAME_KEY, process.getName());
535:
536: super .populateRule(editedRule);
537: }
538:
539: /* (non-Javadoc)
540: * @see org.osbl.agent.gui.DesignContext#changeSpecifiers(org.osbl.agent.model.Rule)
541: */
542: public void changeSpecifiers(Rule rule) {
543:
544: String ruleProcessName = (String) rule.getMetaInformation()
545: .get(RuleContext.PROCESS_NAME_KEY);
546:
547: setConfirmProcessChange(false);
548:
549: // It CAN be NULL for new Rules.
550: if (ruleProcessName != null) {
551: for (int i = 0; i < processComboSpecifier.getItemCount(); i++) {
552: Process comboProcess = (Process) processComboSpecifier
553: .getItemAt(i);
554: if (ruleProcessName.equals(comboProcess.getName()))
555: processComboSpecifier.setSelectedIndex(i);
556: }
557: }
558:
559: setConfirmProcessChange(true);
560: }
561:
562: /* (non-Javadoc)
563: * @see org.osbl.agent.gui.DesignContext#getRuntimeObjectClass()
564: */
565: public Class getRuntimeObjectClass() {
566: return Subject.class;
567: }
568:
569: /* (non-Javadoc)
570: * @see org.osbl.agent.gui.DesignContext#getRuntimeContext()
571: */
572: public RuntimeContext getRuntimeContext() {
573: return new SubjectRuntimeContext();
574: }
575:
576: /* (non-Javadoc)
577: * @see org.osbl.agent.gui.DesignContext#getPreviewControllerInstance()
578: */
579: public PreviewController getPreviewControllerInstance() {
580:
581: PreviewController result = new PreviewController(this ) {
582:
583: SubjectForm form;
584:
585: public ObjectForm getObjectForm() {
586: if (form == null) {
587: form = new SubjectForm();
588: }
589: return form;
590: }
591:
592: public Object getListObject(int rowIdx) {
593: return ((GenericObjectList) designContext
594: .getObjectEditor().getList()).getRow(rowIdx);
595: }
596:
597: public void populateForm(Object targetObject) {
598: form.setSubject((Subject) targetObject);
599: form.getEnvironment().setActive(true);
600: form.setObject(targetObject);
601: }
602:
603: protected void postUpdateActions(Object targetObject) {
604:
605: Subject subject = (Subject) targetObject;
606: Controller controller = getController(targetObject);
607:
608: try {
609: Map<String, String> annotations = new HashMap<String, String>();
610: annotations.put("user", DesignContext
611: .getCurrentUser());
612: Timestamp now = new Timestamp(new Date().getTime());
613:
614: if (subject.getState() == Controller.STATE_RUNNING) {
615: Log logEntry = new Log(subject.getSubjectId(),
616: "AGENT", true, currentRule.toString(),
617: null, annotations, now);
618: controller.log(logEntry);
619: controller.announceSubject(subject
620: .getSubjectId());
621: }
622: } catch (UnknownSubjectException e) {
623: e.printStackTrace();
624: } catch (ControllerException e) {
625: e.printStackTrace();
626: }
627: }
628:
629: protected Controller getController(Object targetObject) {
630: Subject subject = (Subject) targetObject;
631: return ControllerLookup.getInstance().getController(
632: subject.getProcess());
633: }
634:
635: public Object getSubjectInstance(Object targetObject) {
636: return ((Subject) targetObject).getSubject();
637: }
638:
639: };
640:
641: return result;
642: }
643:
644: }
|