/**
 * The form section that displays the available policies for devices and allows users to add and
 * remove policies. This section affects the device definitions document, the master device document
 * when a new policy is created and when a policy removed every device that has a setting for that
 * policy must also be modified.
 */
public class DeviceDefinitionPoliciesSection extends FormSection implements XPathFocusable {
  /** The prefix for property resources associated with this class. */
  private static final String RESOURCE_PREFIX = "DeviceDefinitionPoliciesSection.";

  /** The default minimum width for device definition policies sections. */
  private static final int DEFAULT_MIN_WIDTH =
      DevicesMessages.getInteger(RESOURCE_PREFIX + "minWidth").intValue();

  /** Constant used as the title for this form section */
  private static final String TITLE = DevicesMessages.getString(RESOURCE_PREFIX + "title");

  /** Constant used as the message for this form section */
  private static final String MESSAGE = DevicesMessages.getString(RESOURCE_PREFIX + "message");

  /** The message for the dialog for policy deletion confirmation. */
  private static final MessageFormat POLICY_DELETION_DIALOG_MESSAGE_FORMAT =
      new MessageFormat(DevicesMessages.getString(RESOURCE_PREFIX + "deletePolicyDialog.message"));

  /** The title for the dialog for policy deletion confirmation. */
  private static final String POLICY_DELETION_DIALOG_TITLE =
      DevicesMessages.getString(RESOURCE_PREFIX + "deletePolicyDialog.title");

  /** The text for the Yes button of the dialog for policy deletion confirmation. */
  private static final String POLICY_DELETION_DIALOG_YES_TEXT =
      DevicesMessages.getString(RESOURCE_PREFIX + "deletePolicyDialog.yes");

  /** The text for the No button of the dialog for policy deletion confirmation. */
  private static final String POLICY_DELETION_DIALOG_NO_TEXT =
      DevicesMessages.getString(RESOURCE_PREFIX + "deletePolicyDialog.no");

  /** The original delete actions that will be restored when this section loses focus. */
  private IAction origDelete;

  /** The Delete Policy action. */
  private Action deletePolicyAction;

  /** The new policy action. */
  private Action newPolicyAction;

  /** The categoriesComposite control. */
  private CategoriesComposite categoriesComposite;

  /** The DeviceEditorContext associated with this section. */
  private final DeviceEditorContext context;

  /** Construct a new CategoriesSection. */
  // rest of javadoc inherited
  public DeviceDefinitionPoliciesSection(Composite parent, int style, DeviceEditorContext context) {
    super(parent, style);

    setMinWidth(DEFAULT_MIN_WIDTH);
    this.context = context;
    DeviceRepositoryAccessorManager dram = context.getDeviceRepositoryAccessorManager();

    Section section = SectionFactory.createSection(this, SWT.NONE, TITLE, MESSAGE);
    GridData data = new GridData(GridData.FILL_BOTH);
    section.setLayoutData(data);

    categoriesComposite = new CategoriesComposite(section, CategoriesComposite.POLICIES, dram);
    section.setClient(categoriesComposite);

    // Set up a focus listener for maintaining global actions.
    categoriesComposite
        .getTreeViewer()
        .getControl()
        .addFocusListener(
            new FocusListener() {
              public void focusGained(FocusEvent event) {
                IActionBars actionBars =
                    DeviceDefinitionPoliciesSection.this.context.getActionBars();
                origDelete = actionBars.getGlobalActionHandler(IWorkbenchActionConstants.DELETE);
                actionBars.setGlobalActionHandler(
                    IWorkbenchActionConstants.DELETE, deletePolicyAction);
                actionBars.updateActionBars();
              }

              public void focusLost(FocusEvent event) {
                IActionBars actionBars =
                    DeviceDefinitionPoliciesSection.this.context.getActionBars();
                actionBars.setGlobalActionHandler(IWorkbenchActionConstants.DELETE, origDelete);
                actionBars.updateActionBars();
              }
            });

    // We use the selection manager for event handling because it allows
    // use to use filters which we need for resolving category elements
    // for use with ODOMAction enablement.
    categoriesComposite.addSelectionChangedListener(context.getODOMSelectionManager());

    data = new GridData(GridData.FILL_BOTH);
    categoriesComposite.setLayoutData(data);

    createActions();
    createContextMenu();

    createButtons(this, SWT.NONE);
  }

  /** Create the buttons that allow users to invoke actions on this section. */
  private void createButtons(Composite parent, int style) {
    Composite buttonComposite = new Composite(parent, SWT.NONE);
    GridLayout layout = new GridLayout(2, false);
    buttonComposite.setLayout(layout);
    buttonComposite.setBackground(getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
    new ActionButton(buttonComposite, style, newPolicyAction);
    new ActionButton(buttonComposite, style, deletePolicyAction);
  }

  // javadoc inherited
  public boolean setFocus(XPath path) {
    // todo implement this method
    return false;
  }

  /** Create the actions. */
  private void createActions() {
    // New policy
    ODOMActionCommand command =
        new ODOMActionCommand() {
          public boolean enable(ODOMActionDetails details) {
            Element selectedCategory = categoriesComposite.getSelectedCategoryElement();
            boolean enabled = selectedCategory != null;
            if (enabled) {
              // New policy is always enabled in admin mode when there
              // is a selection.
              enabled = context.isAdminProject();
              if (!enabled) {
                String categoryName =
                    selectedCategory.getAttributeValue(
                        DeviceRepositorySchemaConstants.CATEGORY_NAME_ATTRIBUTE);
                enabled = categoryName.equals(DeviceRepositorySchemaConstants.CUSTOM_CATEGORY_NAME);
              }
            }
            return enabled;
          }

          public void run(ODOMActionDetails details) {
            NewDevicePolicyWizard wizard =
                new NewDevicePolicyWizard(getShell(), context.getDeviceRepositoryAccessorManager());
            wizard.open();
            String policyName = wizard.getPolicyName();
            // If the custom category is selected then prefix the policy
            // name with the custom prefix to enable it to be identified
            // as a custom policy.
            if (categoriesComposite
                .getSelectedCategoryElement()
                .getAttributeValue(DeviceRepositorySchemaConstants.CATEGORY_NAME_ATTRIBUTE)
                .equals(DeviceRepositorySchemaConstants.CUSTOM_CATEGORY_NAME)) {
              StringBuffer buffer =
                  new StringBuffer(EclipseDeviceRepository.getCustomPolicyNamePrefix());
              buffer.append(policyName);
              policyName = buffer.toString();
            }
            PolicyTypeComposition composition = wizard.getPolicyTypeComposition();
            PolicyType type = wizard.getPolicyType();
            if (policyName != null && composition != null && type != null) {
              // we don't allow policy creation to
              // be undoable
              try {
                context.getUndoRedoManager().enable(false);
                addNewPolicyToRepository(policyName, composition, type);
              } finally {
                context.getUndoRedoManager().enable(true);
              }
              selectNewPolicy(policyName);
            }
          }
        };

    newPolicyAction =
        new ODOMAction(
            command,
            context,
            null,
            DevicesMessages.getResourceBundle(),
            RESOURCE_PREFIX + "newPolicy.");

    // Delete policy
    command =
        new ODOMActionCommand() {
          public boolean enable(ODOMActionDetails details) {
            boolean enabled = categoriesComposite.getSelectedPolicyElement() != null;

            if (enabled) {
              // New policy is always enabled in admin mode when there
              // is a selection.
              enabled = context.isAdminProject();
              if (!enabled) {
                String categoryName =
                    categoriesComposite
                        .getSelectedCategoryElement()
                        .getAttributeValue(DeviceRepositorySchemaConstants.CATEGORY_NAME_ATTRIBUTE);
                enabled = categoryName.equals(DeviceRepositorySchemaConstants.CUSTOM_CATEGORY_NAME);
              }
            }
            return enabled;
          }

          public void run(ODOMActionDetails details) {
            final String selectedPolicy =
                details
                    .getElement(0)
                    .getAttributeValue(DeviceRepositorySchemaConstants.POLICY_NAME_ATTRIBUTE);
            final String dialogMessage =
                POLICY_DELETION_DIALOG_MESSAGE_FORMAT.format(new Object[] {selectedPolicy});

            // Get the shell for the dialog.
            final Shell shell = categoriesComposite.getShell();

            // Create the confirmation dialog for the policy deletion.
            // The Yes button is at index 0 and has focus. There is no
            // image.
            final MessageDialog dialog =
                new MessageDialog(
                    shell,
                    POLICY_DELETION_DIALOG_TITLE,
                    null,
                    dialogMessage,
                    MessageDialog.INFORMATION,
                    new String[] {POLICY_DELETION_DIALOG_YES_TEXT, POLICY_DELETION_DIALOG_NO_TEXT},
                    0);
            // Open the dialog.
            dialog.open();

            // Only delete the policy if the user has confirmed the action
            // by pressing the Yes button at index 0.
            if (dialog.getReturnCode() == 0) {
              // Delete takes a while so use a BusyIndicator.
              BusyIndicator.showWhile(
                  shell.getDisplay(),
                  new Runnable() {
                    public void run() {
                      // BusyIndicator should display a busy
                      // cursor automatically, but for some
                      // reason does not do so here (it works
                      // elsewhere within MCS). Experimentation
                      // showed that manually setting a busy
                      // cursor with Display.asyncExec or
                      // Display.syncExec still did not produce
                      // a busy cursor. Strangely, the only
                      // combination that did work was manually
                      // setting the busy cursor with
                      // BusyIndicator.showWhile!
                      // Also, NOT using any of Display.syncExec,
                      // Display.asyncExec and BusyIndicator,
                      // but running the code "as is" with manual
                      // setting of the cursor also did NOT work.
                      // @todo This is odd. BusyIndicator is broken?
                      final Cursor busyCursor = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);

                      shell.setCursor(busyCursor);
                      try {
                        // we don't allow policy deletions to
                        // be undoable
                        context.getUndoRedoManager().enable(false);
                        String policyName =
                            categoriesComposite
                                .getSelectedPolicyElement()
                                .getAttributeValue(
                                    DeviceRepositorySchemaConstants.POLICY_NAME_ATTRIBUTE);
                        context.getDeviceRepositoryAccessorManager().cleansePolicy(policyName);
                      } catch (RepositoryException e) {
                        EclipseCommonPlugin.handleError(ABPlugin.getDefault(), e);
                      } finally {
                        // ensure the undo redo manager is enabled.
                        context.getUndoRedoManager().enable(true);

                        // Restore the shell's default cursor
                        // and dispose of the busy cursor
                        // resources.
                        shell.setCursor(null);
                      }
                    }
                  });
            }
          }
        };

    deletePolicyAction =
        new ODOMAction(
            command,
            context,
            null,
            DevicesMessages.getResourceBundle(),
            RESOURCE_PREFIX + "deletePolicy.");
  }

  /**
   * Creates the context menu that is associated with the tree.
   *
   * <p><strong>The {@link #createActions} method must have been invoked prior to this
   * method</strong>
   */
  private void createContextMenu() {
    // Create menu manager.
    MenuManager menuManager = new MenuManager();
    menuManager.add(newPolicyAction);
    menuManager.add(new Separator());
    menuManager.add(deletePolicyAction);

    // Create the menu and add it to the tree.
    final Tree tree = categoriesComposite.getTreeViewer().getTree();
    Menu menu = menuManager.createContextMenu(tree);
    tree.setMenu(menu);
  }

  /**
   * Add a new policy to the device repository in use. This will add the policy to the definitions
   * document and to the master device file.
   *
   * @param policyName the name of the new policy. Cannot be null.
   * @param composition the PolicyTypeComposition of the new policy
   * @param type the PolicyType of the new policy
   * @throws IllegalArgumentException if the named policy already exists or if any of the arguments
   *     are null.
   */
  private void addNewPolicyToRepository(
      String policyName, PolicyTypeComposition composition, PolicyType type) {
    if (policyName == null) {
      throw new IllegalArgumentException("Cannot be null: " + policyName);
    }
    if (composition == null) {
      throw new IllegalArgumentException("Cannot be null: " + composition);
    }
    if (type == null) {
      throw new IllegalArgumentException("Cannot be null: " + type);
    }
    String masterDeviceName = context.getDeviceRepositoryAccessorManager().retrieveRootDeviceName();
    boolean policyExists =
        context.getDeviceRepositoryAccessorManager().retrievePolicy(masterDeviceName, policyName)
            != null;

    if (policyExists) {
      throw new IllegalArgumentException(
          "Policy " + policyName + " already exists. Aborting new policy.");
    }

    // Add the policy to the definitions document
    Element category = categoriesComposite.getSelectedCategoryElement();
    Element policy =
        context
            .getODOMFactory()
            .element(DeviceRepositorySchemaConstants.POLICY_ELEMENT_NAME, category.getNamespace());
    policy.setAttribute(DeviceRepositorySchemaConstants.POLICY_NAME_ATTRIBUTE, policyName);
    category.addContent(policy);
    composition.addTypeElement(policy, type, context.getODOMFactory());

    // Add the policy to the master device
    Element masterDevice =
        context.getDeviceRepositoryAccessorManager().retrieveDeviceElement(masterDeviceName);

    StringBuffer xPathBuffer = new StringBuffer();
    xPathBuffer
        .append("//")
        . //$NON-NLS-1$
        append(MCSNamespace.DEVICE.getPrefix())
        .append(':')
        .append(DeviceRepositorySchemaConstants.POLICIES_ELEMENT_NAME);
    XPath policiesXPath = new XPath(xPathBuffer.toString(), new Namespace[] {MCSNamespace.DEVICE});

    try {
      Element policies = policiesXPath.selectSingleElement(masterDevice);
      composition.addDefaultPolicyValue(
          policies,
          policyName,
          type,
          context.getODOMFactory(),
          context.getDeviceRepositoryAccessorManager());
    } catch (XPathException e) {
      EclipseCommonPlugin.handleError(ABPlugin.getDefault(), e);
    }
  }

  /**
   * Get the name of the selected policy, if any, from this section.
   *
   * @return the selected policy name or null if no policy is selected.
   */
  public Element getSelectedPolicyElement() {
    return categoriesComposite.getSelectedPolicyElement();
  }

  /**
   * Get the name of the selected category. If a policy is selected then this method will return the
   * name of the category that the policy belongs to.
   *
   * @return the selected category name or null if no category or policy is selected.
   */
  public String getSelectedCategoryName() {
    Element category = categoriesComposite.getSelectedCategoryElement();
    return (category == null)
        ? null
        : category.getAttributeValue(DeviceRepositorySchemaConstants.CATEGORY_NAME_ATTRIBUTE);
  }

  /**
   * Get the name of the selected policy, if any, from this section.
   *
   * @return the selected policy name or null if no policy is selected.
   */
  public String getSelectedPolicy() {
    Element element = categoriesComposite.getSelectedPolicyElement();
    return element == null
        ? null
        : element.getAttributeValue(DeviceRepositorySchemaConstants.POLICY_NAME_ATTRIBUTE);
  }

  /** Add a SelectionChange listener that is notified when policy selection changes. */
  public void addSelectionChangedListener(ISelectionChangedListener listener) {
    categoriesComposite.addSelectionChangedListener(listener);
  }

  /** Remove a SelectionChange listener that is notified when policy selection changes. */
  public void removeSelectionChangedListener(ISelectionChangedListener listener) {
    categoriesComposite.removeSelectionChangedListener(listener);
  }

  /**
   * Select's the new named policy element in the tree.
   *
   * @param newPolicyName the name of the new policy to select.
   */
  private void selectNewPolicy(final String newPolicyName) {
    // Create the XPath for selecting the new policy element just created
    // from the definitions document.
    final StringBuffer newPolicyXPathBuffer = new StringBuffer();
    newPolicyXPathBuffer
        .append("//")
        .append(MCSNamespace.DEVICE_DEFINITIONS.getPrefix())
        .append(':')
        .append(DeviceRepositorySchemaConstants.POLICY_ELEMENT_NAME)
        .append("[@")
        .append(DeviceRepositorySchemaConstants.POLICY_NAME_ATTRIBUTE)
        .append("=\"")
        .append(newPolicyName)
        .append("\"]");
    final XPath newPolicyXPath =
        new XPath(
            newPolicyXPathBuffer.toString(), new Namespace[] {MCSNamespace.DEVICE_DEFINITIONS});

    Element newPolicyElement = null;
    try {
      // Get the definitions root for the XPath search.
      final Element definitionsRoot =
          context
              .getDeviceRepositoryAccessorManager()
              .getDeviceDefinitionsDocument()
              .getRootElement();
      // Retrieve the new policy element.
      newPolicyElement = newPolicyXPath.selectSingleElement(definitionsRoot);
    } catch (XPathException e) {
      EclipseCommonPlugin.handleError(ABPlugin.getDefault(), e);
    }

    TreeViewer treeViewer = categoriesComposite.getTreeViewer();

    // Expand the tree to the new policy element's level and select it.
    treeViewer.expandToLevel(newPolicyElement, 1);
    treeViewer.setSelection(new StructuredSelection(newPolicyElement), true);
  }
}
/** Composite that displays the form sections that constitute the device overview page */
public class DeviceOverviewForm extends Composite implements XPathFocusable {

  /** Used for logging */
  private static final LogDispatcher LOGGER =
      LocalizationFactory.createLogger(DeviceOverviewForm.class);

  /** Used to retrieve localized exception messages. */
  private static final ExceptionLocalizer EXCEPTION_LOCALIZER =
      LocalizationFactory.createExceptionLocalizer(DeviceOverviewForm.class);

  /** The prefix for resources associated with this class. */
  private static final String RESOURCE_PREFIX = "DeviceOverviewForm.";

  /** Constant for device overview forms margin height */
  private static final int MARGIN_HEIGHT =
      EditorMessages.getInteger("Editor.marginHeight").intValue();

  /** Constant for device overview forms margin width */
  private static final int MARGIN_WIDTH =
      EditorMessages.getInteger("Editor.marginWidth").intValue();

  /** Constant for device overview forms horizontal spacing */
  private static final int HORIZONTAL_SPACING =
      EditorMessages.getInteger("Editor.horizontalSpacing").intValue();

  /** Constant for device overview forms vertical spacing */
  private static final int VERTICAL_SPACING =
      EditorMessages.getInteger("Editor.verticalSpacing").intValue();

  /** The text for the Restore Defaults button. */
  private static final String RESTORE_DEFAULTS_TEXT =
      DevicesMessages.getString(RESOURCE_PREFIX + "restoreDefaults.text");

  /** Create a filter so that only device elements are included in the selection. */
  private static final ODOMSelectionFilter DEVICE_FILTER =
      new ODOMSelectionFilter(
          null,
          new String[] {DeviceRepositorySchemaConstants.DEVICE_ELEMENT_NAME},
          new ODOMSelectionFilterConfiguration(true, true));

  /** FormSection that displays the hierarchy document in a tree format. */
  private DeviceHierarchySection deviceHierarchySection;

  /** The ODOMEditor context for the device repository */
  private DeviceEditorContext context;

  /** The Restore Defaults button. */
  private Button restoreButton;

  /** Maintain the selected element so that we restore it if necessary (via the restore action). */
  private Element deviceIDElement;

  /**
   * Initializes a <code>DeviceOverviewForm</code> with the given arguments
   *
   * @param parent the parent composite
   * @param style a bitset used to specify any styles
   * @param context the DeviceEditorContext.
   */
  public DeviceOverviewForm(Composite parent, int style, DeviceEditorContext context) {
    super(parent, style);
    this.context = context;
    createDisplayArea();
  }

  /** Creates the display area for this form */
  private void createDisplayArea() {
    // create the top level layout for the form
    GridLayout layout = new GridLayout();
    layout.marginHeight = MARGIN_HEIGHT;
    layout.marginWidth = MARGIN_WIDTH;
    layout.verticalSpacing = VERTICAL_SPACING;
    layout.horizontalSpacing = HORIZONTAL_SPACING;
    GridData data = new GridData(GridData.FILL_BOTH);
    setLayout(layout);
    setLayoutData(data);
    // set the background color to white
    Color white = getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
    setBackground(white);

    // add an alerts and actions section
    AlertsActionsSection alerts = new AlertsActionsSection(this, SWT.NONE, context);

    alerts.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

    // add a two column contianer for the form sections. The first
    // column will display the DeviceHierarchy section and the second
    // column will display the primary and secondary pattern sections.
    Composite formContainer = new Composite(this, SWT.NONE);
    GridLayout formLayout = new GridLayout(2, false);
    formLayout.marginWidth = 0;
    formLayout.marginHeight = 0;
    formLayout.horizontalSpacing = HORIZONTAL_SPACING;
    GridData formData = new GridData(GridData.FILL_BOTH);
    formContainer.setLayout(formLayout);
    formContainer.setLayoutData(formData);
    formContainer.setBackground(white);

    // add the hierarchy section to the formContainer
    deviceHierarchySection = new DeviceHierarchySection(formContainer, SWT.NONE, context);
    GridData hierarchyData = new GridData(GridData.FILL_VERTICAL);
    deviceHierarchySection.setLayoutData(hierarchyData);

    // add a scroll area for the patterns sections
    ScrolledComposite controlsScroller =
        new ScrolledComposite(formContainer, SWT.H_SCROLL | SWT.V_SCROLL);
    controlsScroller.setLayout(new FillLayout());
    controlsScroller.setLayoutData(new GridData(GridData.FILL_BOTH));
    controlsScroller.setExpandHorizontal(true);
    controlsScroller.setExpandVertical(true);
    controlsScroller.setAlwaysShowScrollBars(false);
    controlsScroller.setBackground(white);

    // add the container for the patterns sections
    Composite patternContainer = new Composite(controlsScroller, SWT.NONE);
    GridLayout patternLayout = new GridLayout();
    patternLayout.marginHeight = 0;
    patternLayout.marginWidth = 0;
    patternLayout.verticalSpacing = VERTICAL_SPACING;
    GridData patternData = new GridData(GridData.FILL_BOTH);
    patternContainer.setLayout(patternLayout);
    patternContainer.setLayoutData(patternData);
    patternContainer.setBackground(white);

    // Add the primary patterns section
    PrimaryPatternsSection primaryPatterns =
        new PrimaryPatternsSection(patternContainer, SWT.NONE, context);
    primaryPatterns.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

    // Add the secondary patterns section
    SecondaryPatternsSection secondaryPatterns =
        new SecondaryPatternsSection(patternContainer, SWT.NONE, context);
    secondaryPatterns.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

    createRestoreMechanism(patternContainer);

    TACPatternsSection TACPatterns = new TACPatternsSection(patternContainer, SWT.NONE, context);
    TACPatterns.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

    patternContainer.setSize(patternContainer.computeSize(SWT.DEFAULT, SWT.DEFAULT));
    patternContainer.layout();
    controlsScroller.setMinSize(patternContainer.computeSize(SWT.DEFAULT, SWT.DEFAULT));

    controlsScroller.setContent(patternContainer);

    layout();
  }

  /**
   * Get the name of the selected device.
   *
   * @return the name of the selected device or null if none selected.
   */
  String getSelectedDeviceName() {
    String selectedDeviceName = null;
    if (deviceIDElement != null) {
      selectedDeviceName =
          deviceIDElement.getAttributeValue(DeviceRepositorySchemaConstants.DEVICE_NAME_ATTRIBUTE);
    }

    return selectedDeviceName;
  }

  /**
   * Add the restore button and the standard element handling facility.
   *
   * @param parent the parent composite for the resotre button.
   */
  private void createRestoreMechanism(Composite parent) {
    restoreButton = new Button(parent, SWT.NONE);
    restoreButton.setText(RESTORE_DEFAULTS_TEXT);
    restoreButton.setEnabled(!context.isAdminProject());
    restoreButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));
    restoreButton.addSelectionListener(
        new SelectionListener() {
          public void widgetSelected(SelectionEvent e) {
            widgetDefaultSelected(e);
          }

          public void widgetDefaultSelected(SelectionEvent event) {
            if (deviceIDElement != null) {
              ((DeviceODOMElement) deviceIDElement).restore();
            }
          }
        });

    // Add a listener to the selection manager which responds to device
    // element selections.
    context
        .getODOMSelectionManager()
        .addSelectionListener(
            new ODOMElementSelectionListener() {

              public void selectionChanged(ODOMElementSelectionEvent event) {
                ODOMElementSelection selection = event.getSelection();

                if (!selection.isEmpty()) {
                  Element deviceElement = (Element) selection.getFirstElement();
                  // Retrieve the device name.
                  final String deviceName =
                      deviceElement.getAttributeValue(
                          DeviceRepositorySchemaConstants.DEVICE_NAME_ATTRIBUTE);

                  deviceIDElement =
                      context
                          .getDeviceRepositoryAccessorManager()
                          .retrieveDeviceIdentification(deviceName);

                  if (deviceIDElement == null) {
                    Object params =
                        new Object[] {
                          deviceName,
                          context.getDeviceRepositoryAccessorManager().getDeviceRepositoryName()
                        };
                    LOGGER.error("device-not-found", params);
                    String message = EXCEPTION_LOCALIZER.format("device-not-found", params);
                    throw new UndeclaredThrowableException(new RepositoryException(message));
                  } else {
                    DeviceODOMElement parent = (DeviceODOMElement) deviceIDElement.getParent();
                    if (parent == null) {
                      LOGGER.error("device-no-parent", deviceName);
                      String message = EXCEPTION_LOCALIZER.format("device-no-parent", deviceName);
                      throw new UndeclaredThrowableException(new RepositoryException(message));
                    } else {
                      parent.submitRestorableName(deviceName);
                    }
                  }
                } else {
                  deviceIDElement = null;
                }
              }
            },
            DEVICE_FILTER);
  }

  // javadoc inherited
  public boolean setFocus(XPath path) {
    // todo implement this
    return false;
  }
}
  /** Create the actions. */
  private void createActions() {
    // New policy
    ODOMActionCommand command =
        new ODOMActionCommand() {
          public boolean enable(ODOMActionDetails details) {
            Element selectedCategory = categoriesComposite.getSelectedCategoryElement();
            boolean enabled = selectedCategory != null;
            if (enabled) {
              // New policy is always enabled in admin mode when there
              // is a selection.
              enabled = context.isAdminProject();
              if (!enabled) {
                String categoryName =
                    selectedCategory.getAttributeValue(
                        DeviceRepositorySchemaConstants.CATEGORY_NAME_ATTRIBUTE);
                enabled = categoryName.equals(DeviceRepositorySchemaConstants.CUSTOM_CATEGORY_NAME);
              }
            }
            return enabled;
          }

          public void run(ODOMActionDetails details) {
            NewDevicePolicyWizard wizard =
                new NewDevicePolicyWizard(getShell(), context.getDeviceRepositoryAccessorManager());
            wizard.open();
            String policyName = wizard.getPolicyName();
            // If the custom category is selected then prefix the policy
            // name with the custom prefix to enable it to be identified
            // as a custom policy.
            if (categoriesComposite
                .getSelectedCategoryElement()
                .getAttributeValue(DeviceRepositorySchemaConstants.CATEGORY_NAME_ATTRIBUTE)
                .equals(DeviceRepositorySchemaConstants.CUSTOM_CATEGORY_NAME)) {
              StringBuffer buffer =
                  new StringBuffer(EclipseDeviceRepository.getCustomPolicyNamePrefix());
              buffer.append(policyName);
              policyName = buffer.toString();
            }
            PolicyTypeComposition composition = wizard.getPolicyTypeComposition();
            PolicyType type = wizard.getPolicyType();
            if (policyName != null && composition != null && type != null) {
              // we don't allow policy creation to
              // be undoable
              try {
                context.getUndoRedoManager().enable(false);
                addNewPolicyToRepository(policyName, composition, type);
              } finally {
                context.getUndoRedoManager().enable(true);
              }
              selectNewPolicy(policyName);
            }
          }
        };

    newPolicyAction =
        new ODOMAction(
            command,
            context,
            null,
            DevicesMessages.getResourceBundle(),
            RESOURCE_PREFIX + "newPolicy.");

    // Delete policy
    command =
        new ODOMActionCommand() {
          public boolean enable(ODOMActionDetails details) {
            boolean enabled = categoriesComposite.getSelectedPolicyElement() != null;

            if (enabled) {
              // New policy is always enabled in admin mode when there
              // is a selection.
              enabled = context.isAdminProject();
              if (!enabled) {
                String categoryName =
                    categoriesComposite
                        .getSelectedCategoryElement()
                        .getAttributeValue(DeviceRepositorySchemaConstants.CATEGORY_NAME_ATTRIBUTE);
                enabled = categoryName.equals(DeviceRepositorySchemaConstants.CUSTOM_CATEGORY_NAME);
              }
            }
            return enabled;
          }

          public void run(ODOMActionDetails details) {
            final String selectedPolicy =
                details
                    .getElement(0)
                    .getAttributeValue(DeviceRepositorySchemaConstants.POLICY_NAME_ATTRIBUTE);
            final String dialogMessage =
                POLICY_DELETION_DIALOG_MESSAGE_FORMAT.format(new Object[] {selectedPolicy});

            // Get the shell for the dialog.
            final Shell shell = categoriesComposite.getShell();

            // Create the confirmation dialog for the policy deletion.
            // The Yes button is at index 0 and has focus. There is no
            // image.
            final MessageDialog dialog =
                new MessageDialog(
                    shell,
                    POLICY_DELETION_DIALOG_TITLE,
                    null,
                    dialogMessage,
                    MessageDialog.INFORMATION,
                    new String[] {POLICY_DELETION_DIALOG_YES_TEXT, POLICY_DELETION_DIALOG_NO_TEXT},
                    0);
            // Open the dialog.
            dialog.open();

            // Only delete the policy if the user has confirmed the action
            // by pressing the Yes button at index 0.
            if (dialog.getReturnCode() == 0) {
              // Delete takes a while so use a BusyIndicator.
              BusyIndicator.showWhile(
                  shell.getDisplay(),
                  new Runnable() {
                    public void run() {
                      // BusyIndicator should display a busy
                      // cursor automatically, but for some
                      // reason does not do so here (it works
                      // elsewhere within MCS). Experimentation
                      // showed that manually setting a busy
                      // cursor with Display.asyncExec or
                      // Display.syncExec still did not produce
                      // a busy cursor. Strangely, the only
                      // combination that did work was manually
                      // setting the busy cursor with
                      // BusyIndicator.showWhile!
                      // Also, NOT using any of Display.syncExec,
                      // Display.asyncExec and BusyIndicator,
                      // but running the code "as is" with manual
                      // setting of the cursor also did NOT work.
                      // @todo This is odd. BusyIndicator is broken?
                      final Cursor busyCursor = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);

                      shell.setCursor(busyCursor);
                      try {
                        // we don't allow policy deletions to
                        // be undoable
                        context.getUndoRedoManager().enable(false);
                        String policyName =
                            categoriesComposite
                                .getSelectedPolicyElement()
                                .getAttributeValue(
                                    DeviceRepositorySchemaConstants.POLICY_NAME_ATTRIBUTE);
                        context.getDeviceRepositoryAccessorManager().cleansePolicy(policyName);
                      } catch (RepositoryException e) {
                        EclipseCommonPlugin.handleError(ABPlugin.getDefault(), e);
                      } finally {
                        // ensure the undo redo manager is enabled.
                        context.getUndoRedoManager().enable(true);

                        // Restore the shell's default cursor
                        // and dispose of the busy cursor
                        // resources.
                        shell.setCursor(null);
                      }
                    }
                  });
            }
          }
        };

    deletePolicyAction =
        new ODOMAction(
            command,
            context,
            null,
            DevicesMessages.getResourceBundle(),
            RESOURCE_PREFIX + "deletePolicy.");
  }