package org.js.model.workflow.ui; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Set; import org.eclipse.emf.common.util.EList; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jwt.meta.model.core.Model; import org.eclipse.jwt.meta.model.processes.Action; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Shell; import org.js.model.feature.Attribute; import org.js.model.feature.DiscreteDomain; import org.js.model.feature.Domain; import org.js.model.feature.DomainValue; import org.js.model.feature.Feature; import org.js.model.feature.FeatureModel; import org.js.model.feature.FeatureState; import org.js.model.feature.ReferenceResolverUtil; import org.js.model.feature.csp.FeatureModelAnalyzer; import org.js.model.feature.edit.FeatureLabelProvider; import org.js.model.rbac.AttributeOperation; import org.js.model.rbac.ConfigurationOperation; import org.js.model.rbac.FeatureOperation; import org.js.model.rbac.Permission; import org.js.model.rbac.RBACService; import org.js.model.rbac.RbacHelper; import org.js.model.rbac.Role; import org.js.model.workflow.EFMContainer; import org.js.model.workflow.Log; import org.js.model.workflow.State; import org.js.model.workflow.StateEnum; import org.js.model.workflow.util.WorkflowConfUtil; import org.js.model.workflow.util.WorkflowModelUtil; import org.js.model.workflow.util.WorkflowUtil; public class StakeholderConfigUIShell extends Shell { ArrayList<Group> configGroups = new ArrayList<Group>(); HashMap<Control, ConfigurationOperation> decisionMap = new HashMap<Control, ConfigurationOperation>(); HashMap<Group, Feature> featureMap = new HashMap<Group, Feature>(); HashMap<Group, Attribute> attributeMap = new HashMap<Group, Attribute>(); Action selectedAction = null; Role selectedRole = null; State selectedState = null; FeatureModel featureModel = null; Log log = null; Set<Permission> permissions = null; Composite composite_config; Button button_ok, button_cancel; /** * Create the shell. * * @param display */ public StakeholderConfigUIShell(Model workflowModel, Display display, Role role, Action action, State state) { super(display, SWT.SHELL_TRIM); selectedAction = action; selectedRole = role; selectedState = state; RBACService rbacService = new RBACService(); permissions = rbacService.getAllRolePermissions(selectedRole); log = (Log) WorkflowConfUtil.getAspectInstance(selectedAction, WorkflowConfUtil.LOG_ASPECT); initUI(); checkSelectionStates(workflowModel); createContents(); setOkButtonEnabled(); } private void handlePermissions() { for (Permission permission : permissions) { if (permission instanceof FeatureOperation) { FeatureOperation operation = (FeatureOperation) permission; handleFeatureOperation(operation); } else if (permission instanceof AttributeOperation) { AttributeOperation operation = (AttributeOperation) permission; handleAttributeOperation(operation); } } } private void handleFeatureOperation(FeatureOperation operation) { // feature in the original efm Feature oldFeature = operation.getFeature(); String featureName = oldFeature.getId(); String groupName = featureName; // feature in the new efm Feature configuredFeature = ReferenceResolverUtil.findFeature(featureName, featureModel); // get the configuration group Group configGroup = null; for (Group group : configGroups) { if (group.getText().equals(groupName)) { configGroup = group; break; } } if (configGroup == null) { configGroup = new Group(composite_config, SWT.NONE); configGroup.setText(groupName); configGroup.setLayout(new FillLayout(SWT.HORIZONTAL)); configGroups.add(configGroup); featureMap.put(configGroup, configuredFeature); } // add radio button Button radioButton = new Button(configGroup, SWT.CHECK); radioButton.setSelection(false); if (RbacHelper.isSelectFeatureOperation(operation)) { radioButton.setText("select"); } else if (RbacHelper.isDeselectFeatureOperation(operation)) { radioButton.setText("deselect"); } // if configuration action is running, check whether the // feature is configured by the previous role if (StateEnum.RUNNING.equals(selectedState.getState())) { switch (configuredFeature.getConfigurationState().getValue()) { case FeatureState.UNBOUND_VALUE: break; case FeatureState.SELECTED_VALUE: radioButton.setEnabled(false); if (RbacHelper.isSelectFeatureOperation(operation)) { radioButton.setSelection(true); } break; case FeatureState.DESELECTED_VALUE: radioButton.setEnabled(false); if (RbacHelper.isDeselectFeatureOperation(operation)) { radioButton.setSelection(true); } break; } } // if configuration action is finished, button is inactive if (StateEnum.COMPLETED.equals(selectedState.getState())) { radioButton.setEnabled(false); if (log.getConfigurationOperations().contains(operation)) { radioButton.setSelection(true); } } decisionMap.put(radioButton, (ConfigurationOperation) operation); } private void handleAttributeOperation(AttributeOperation operation) { Feature oldFeature = operation.getFeature(); String featureName = oldFeature.getId(); Feature newFeature = ReferenceResolverUtil.findFeature(featureName, featureModel); Attribute oldAttribute = operation.getAttribute(); String attributeName = oldAttribute.getName(); String groupName = featureName + ": " + attributeName; // get the attribute Attribute configuredAttribute = ReferenceResolverUtil.findAttributeForFeature(attributeName, newFeature); // get the configuration group Group configGroup = null; for (Group group : configGroups) { if (group.getText().equals(groupName)) { configGroup = group; break; } } if (configGroup == null) { configGroup = new Group(composite_config, SWT.NONE); configGroup.setText(groupName); configGroup.setLayout(new FillLayout(SWT.HORIZONTAL)); configGroups.add(configGroup); attributeMap.put(configGroup, configuredAttribute); } Combo valueCombo = new Combo(configGroup, SWT.READ_ONLY); valueCombo.setItems(getAttributeValues(configuredAttribute)); valueCombo.setBounds(0, 0, 200, 200); // for (AttributeValueOperation attributeDec : operation.getValueOperations()) { // Button radioButton = new Button(configGroup, SWT.RADIO); // if (RbacHelper.isSelectDomainValueOperation(attributeDec)) { // radioButton.setText("select " + attributeDec.getValue()); // } else if (RbacHelper.isDeselectDomainValueOperation(attributeDec)) { // radioButton.setText("deselect " + attributeDec.getValue()); // } // // if configuration action is running, check whether the attribute is configured // if (StateEnum.RUNNING.equals(selectedState.getState())) { // if (configuredAttribute.getValue() == null || configuredAttribute.getValue().equals("")) {} else { // radioButton.setEnabled(false); // if (RbacHelper.isSelectDomainValueOperation(attributeDec) && // attributeDec.getValue().equals(configuredAttribute.getValue())) { // radioButton.setSelection(true); // } // } // } // // configuration action is finished // if (StateEnum.COMPLETED.equals(selectedState.getState())) { // radioButton.setEnabled(false); // if (log.getConfigurationOperations().contains(attributeDec)) { // radioButton.setSelection(true); // } // } decisionMap.put(valueCombo, operation); // } } private void checkSelectionStates(Model workflowModel) { if (StateEnum.ENABLED.equals(selectedState.getState())) { selectedState.setState(StateEnum.RUNNING); WorkflowModelUtil.setActionState(selectedAction, StateEnum.RUNNING); // trace the previous efm featureModel = WorkflowUtil.tracePreEFM(workflowModel, selectedRole, selectedAction); } else { EFMContainer efmContainer = (EFMContainer) WorkflowConfUtil.getAspectInstance(selectedAction, WorkflowConfUtil.EFM_ASPECT); featureModel = efmContainer.getEfmref(); } // TODO: check bugs // create configuration groups // EList<Permission> permissions = role.getPermissions(); if (featureModel != null) { handlePermissions(); // add listeners for buttons for (Group group : configGroups) { for (Control control : group.getChildren()) { SelectionAdapter adapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { handleConfigurationOperation(e); setOkButtonEnabled(); // handleSelectionLogic((Button) e.getSource(), decisionMap, permissions, featureModel); } }; if (control instanceof Button) { Button button = (Button) control; button.addSelectionListener(adapter); } else if (control instanceof Combo) { Combo combo = (Combo) control; combo.addSelectionListener(adapter); } } } } // TODO: check the feature in a group (optional group!) // add listeners button_ok.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // check whether all decisions are completed String info = "Please finish the selection: "; String temp = ""; boolean allSelected = true; for (Group group : configGroups) { boolean groupSelected = false; // if the group is about attribute if (group.getText().contains(":")) { for (Control control : group.getChildren()) { // attribute must only be set if feature is selected groupSelected = isAttributeConfigured(control); } } else { for (Control control : group.getChildren()) { groupSelected = ((Button) control).getSelection() || groupSelected; } } if (!groupSelected) { temp += group.getText(); if (group.getText().contains(":")) { temp += " (should select one value!)"; } temp += ", "; } allSelected = groupSelected && allSelected; } if (!allSelected) { info += temp; MessageDialog.openInformation(getShell(), "Warning", info); } else { if (selectedState.getState().getValue() == 2) { saveConfigLog(configGroups, selectedRole, selectedAction); WorkflowUtil.handleFM(log, featureModel); WorkflowModelUtil.setActionState(selectedAction, StateEnum.COMPLETED); } dispose(); } } private boolean isAttributeConfigured(Control control) { boolean result = false; ConfigurationOperation configOp = decisionMap.get(control); if (configOp instanceof AttributeOperation) { AttributeOperation attOp = (AttributeOperation) configOp; String featureid = attOp.getFeature().getId(); Feature feature = ReferenceResolverUtil.findFeature(featureid, featureModel); boolean isFeatureSelected = FeatureState.SELECTED.equals(feature.getConfigurationState()); String attributeId = attOp.getAttribute().getName(); Attribute attribute = ReferenceResolverUtil.findAttributeForFeature(attributeId, feature); String attributeValue = attribute.getValue(); boolean isNotEmptyValue = attributeValue != null && !attributeValue.isEmpty(); result = ((isFeatureSelected && isNotEmptyValue) || !isFeatureSelected); } return result; } }); button_cancel.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (selectedState.getState().getValue() == 2) { selectedState.setState(StateEnum.ENABLED); } dispose(); } }); } private String[] getAttributeValues(Attribute attribute) { List<String> valueList = new ArrayList<String>(); String empty = ""; valueList.add(empty); FeatureLabelProvider labelProvider = new FeatureLabelProvider(); Domain domain = attribute.getDomain(); if (domain instanceof DiscreteDomain) { DiscreteDomain discrete = (DiscreteDomain) domain; EList<DomainValue> values = discrete.getValues(); for (DomainValue domainValue : values) { String label = labelProvider.getText(domainValue); valueList.add(label); } } return (String[]) valueList.toArray(new String[valueList.size()]); } private void initUI() { setLayout(new FillLayout(SWT.HORIZONTAL)); Composite composite = new Composite(this, SWT.NONE); composite.setLayout(new GridLayout(1, false)); composite_config = new Composite(composite, SWT.NONE); RowLayout rl_composite_config = new RowLayout(SWT.VERTICAL); rl_composite_config.fill = true; composite_config.setLayout(rl_composite_config); composite_config.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); Composite composite_buttons = new Composite(composite, SWT.NONE); composite_buttons.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); composite_buttons.setLayout(new RowLayout(SWT.HORIZONTAL)); button_ok = new Button(composite_buttons, SWT.NONE); button_ok.setText("Ok"); button_cancel = new Button(composite_buttons, SWT.NONE); button_cancel.setText("Cancel"); } private void setOkButtonEnabled() { button_ok.setEnabled(isFeatureModelSatisfiable()); } /** * Create contents of the shell. */ protected void createContents() { setText("Configuration"); int length = configGroups.size() * 40 + 100; int width = 0; for (Group group : configGroups) { int tempLength = group.getChildren().length; if (width < tempLength) { width = tempLength; } } width = width * 140; setSize(width, length); } public void saveConfigLog(ArrayList<Group> configGroup, Role role, Action action) { Log log = (Log) WorkflowConfUtil.getAspectInstance(action, WorkflowConfUtil.LOG_ASPECT); log.getConfigurationOperations().clear(); for (Group group : configGroup) { for (Control control : group.getChildren()) { if (control instanceof Button) { Button button = (Button) control; if (button.getSelection()) { log.getConfigurationOperations().add(decisionMap.get(control)); } } else if (control instanceof Combo) { Combo combo = (Combo) control; } } } } @Override protected void checkSubclass() { // Disable the check that prevents subclassing of SWT components } public State getState() { return selectedState; } public void handleConfigurationOperation(SelectionEvent e) { Object source = e.getSource(); if (source instanceof Button) { Button button = (Button) source; boolean isSelected = button.getSelection(); ConfigurationOperation configOperation = decisionMap.get(button); checkConfigurationOperation(configOperation, isSelected); if (isSelected) { disableOtherButtonsInFeatureGroup(button); } } else if (source instanceof Combo) { Combo combo = (Combo) source; int index = combo.getSelectionIndex(); String item = combo.getItem(index); ConfigurationOperation configOperation = decisionMap.get(combo); Attribute configuredAttribute = attributeMap.get(combo.getParent()); handleAttributeSelection(configuredAttribute, configOperation, item); } } private void handleAttributeSelection(Attribute configuredAttribute, ConfigurationOperation configOperation, String selectedItem) { String value = parseAttributeValueName(selectedItem); configuredAttribute.setValue(value); } private String parseAttributeValueName(String selectedItem) { String result = ""; String[] split = selectedItem.split(" "); if (split.length >= 1) { String name = split[0]; name.trim(); result = name; } return result; } private void checkConfigurationOperation(ConfigurationOperation op, boolean isSelected) { if (op instanceof FeatureOperation) { FeatureOperation featureOp = (FeatureOperation) op; Feature oldFeature = ((FeatureOperation) featureOp).getFeature(); Feature configuredFeature = ReferenceResolverUtil.findFeature(oldFeature.getId(), featureModel); if (isSelected) { if (RbacHelper.isSelectFeatureOperation(featureOp)) { selectFeature(configuredFeature); } else if (RbacHelper.isDeselectFeatureOperation(featureOp)) { deselectFeature(configuredFeature); } } else { // revert operation on feature model unboundFeature(configuredFeature); } } } private void disableOtherButtonsInFeatureGroup(Button button) { Composite parent = button.getParent(); if (parent instanceof Group) { Group group = (Group) parent; Control[] children = group.getChildren(); for (Control control : children) { if (control instanceof Button) { Button controlButton = (Button) control; if (!controlButton.equals(button)) { if (controlButton.isEnabled()) { controlButton.setSelection(false); } } } } } } private boolean isFeatureModelSatisfiable() { FeatureModelAnalyzer analyzer = new FeatureModelAnalyzer(featureModel); return analyzer.isSatisfiable(); } private void selectFeature(Feature feature) { feature.setConfigurationState(FeatureState.SELECTED); } private void deselectFeature(Feature feature) { feature.setConfigurationState(FeatureState.DESELECTED); } private void unboundFeature(Feature feature) { feature.setConfigurationState(FeatureState.UNBOUND); } // public void handleSelectionLogic(Button button, HashMap<Button, ConfigurationOperation> decisionMap, Set<Permission> permissions, // FeatureModel featureModel) { // if (button.getSelection()) { // ConfigurationOperation configDecision = decisionMap.get(button); // if (configDecision instanceof FeatureOperation) { // Feature oldFeature = ((FeatureOperation) configDecision).getFeature(); // Feature configuredFeature = ReferenceResolverUtil.findFeature(oldFeature.getId(), featureModel); // ArrayList<Feature> mandatoryChildFeatures = WorkflowUtil.getMandatoryChildFeatures(configuredFeature); // ArrayList<Feature> parentFeaures = WorkflowUtil.getParentFeatures(configuredFeature, featureModel); // ArrayList<Feature> groupFeatures = WorkflowUtil.getGroupFeatures(configuredFeature, featureModel); // ArrayList<Feature> constraintRightOperands = WorkflowUtil.getImpliesConstraintRightOperands(configuredFeature, featureModel); // ArrayList<Feature> constraintLeftOperands = WorkflowUtil.getImpliesConstraintLeftOperands(configuredFeature, featureModel); // ArrayList<Feature> childFeatures = WorkflowUtil.getChildFeatures(configuredFeature); // if (RbacHelper.isSelectFeatureOperation(configDecision)) { // for (Group group : featureMap.keySet()) { // Feature feature = featureMap.get(group); // org.js.model.feature.Group featureGroup = WorkflowUtil.getGroup(feature, featureModel); // // 1. set all mandatory sub features into selected // // 2. set parent feature into selected // // 3. set features of related constraints into selected // if (mandatoryChildFeatures.contains(feature) || parentFeaures.contains(feature) // || constraintRightOperands.contains(feature)) { // for (Control control : group.getChildren()) { // if (RbacHelper.isSelectFeatureOperation(decisionMap.get(control))) { // ((Button) control).setSelection(true); // } else { // ((Button) control).setSelection(false); // } // } // } // // 4. set the selected status of the other features in // // the same group // else if (featureGroup != null && WorkflowUtil.getGroupType(featureGroup) == WorkflowUtil.ALTERNATIVE_GROUP // && feature != configuredFeature && groupFeatures.contains(feature)) { // for (Control control : group.getChildren()) { // if (RbacHelper.isSelectFeatureOperation(decisionMap.get(control))) { // ((Button) control).setSelection(false); // } else { // ((Button) control).setSelection(true); // } // } // } // } // } else if (RbacHelper.isDeselectFeatureOperation(configDecision)) { // Feature parentFeature = WorkflowUtil.getParentFeature(configuredFeature, featureModel.getRoot()); // org.js.model.feature.Group featureGroup = WorkflowUtil.getGroup(configuredFeature, featureModel); // for (Group group : featureMap.keySet()) { // Feature feature = featureMap.get(group); // // 1. set all sub features into deselected // // 2. related attributes are deselected // if (childFeatures.contains(feature) || constraintLeftOperands.contains(feature)) { // for (Control control : group.getChildren()) { // if (RbacHelper.isSelectFeatureOperation(decisionMap.get(control))) { // ((Button) control).setSelection(false); // } else { // ((Button) control).setSelection(true); // } // } // } // // 3. if it is mandatory feature and its parent feature // // can not be deselected // if (feature == parentFeature && WorkflowUtil.getGroupType(featureGroup) == WorkflowUtil.MANDATORY_GROUP) { // for (Control control : group.getChildren()) { // if (RbacHelper.isSelectFeatureOperation(decisionMap.get(control))) { // ((Button) control).setSelection(false); // } // } // } // // 4. set the selected status of the other features in // // the same group // if (WorkflowUtil.getGroupType(featureGroup) == WorkflowUtil.OR_GROUP // || WorkflowUtil.getGroupType(featureGroup) == WorkflowUtil.ALTERNATIVE_GROUP) { // if (feature != configuredFeature && groupFeatures.contains(feature)) { // // } // } // } // } // } else if (configDecision instanceof AttributeOperation) { // // related feature is selected // Feature oldFeature = ((AttributeOperation) configDecision).getFeature(); // // } // } // } }