# GroupsPresenter.java

    /*
    ** Module   : GroupsPresenter.java
    ** Abstract : GroupsPresenter represents a controller to manage groups.
    **
    ** Copyright (c) 2017, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ----------------Description----------------------
    ** 001 SBI 20170601 Created initial version.
    */
    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.admin.client.application.home.accounts.groups;

    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Set;
    import java.util.logging.Level;

    import javax.inject.Named;

    import com.goldencode.p2j.admin.client.*;
    import com.goldencode.p2j.admin.client.application.Alarm;
    import com.goldencode.p2j.admin.client.application.PrintingContext;
    import com.goldencode.p2j.admin.client.application.home.HomePresenter;
    import com.goldencode.p2j.admin.client.application.home.ViewStateMachine;
    import com.goldencode.p2j.admin.client.application.home.accounts.AccountsPresenter;
    import com.goldencode.p2j.admin.client.application.home.accounts.UseCaseMode;
    import com.goldencode.p2j.admin.client.application.home.acl.*;
    import com.goldencode.p2j.admin.client.widget.dialog.ModalDialogs;
    import com.goldencode.p2j.admin.shared.AdminServiceAsync;
    import com.goldencode.p2j.admin.GroupDef;
    import com.goldencode.p2j.admin.TaggedName;
    import com.google.gwt.regexp.shared.RegExp;
    import com.google.gwt.user.client.rpc.AsyncCallback;
    import com.google.gwt.user.client.ui.HasEnabled;
    import com.google.inject.Inject;
    import com.google.inject.Provider;
    import com.google.web.bindery.event.shared.EventBus;
    import com.gwtplatform.mvp.client.HasUiHandlers;
    import com.gwtplatform.mvp.client.View;
    import com.gwtplatform.mvp.client.annotations.NameToken;
    import com.gwtplatform.mvp.client.annotations.ProxyStandard;
    import com.gwtplatform.mvp.client.presenter.slots.NestedSlot;
    import com.gwtplatform.mvp.client.proxy.PlaceManager;
    import com.gwtplatform.mvp.client.proxy.ProxyPlace;
    import com.gwtplatform.mvp.shared.proxy.PlaceRequest;

    /**
     * GroupsPresenter represents a controller to manage groups.
     */
    public class GroupsPresenter
    extends AccountsPresenter<GroupsPresenter.MyView, GroupsPresenter.MyProxy>
    implements GroupsUIHandlers
    {
       /**
        * Defines the Group Accounts View states
        */
       public static enum GroupsViewStates
       implements ViewStateMachine.TransitionState
       {
          /** Groups list has been refreshed. */
          GROUPS_LIST_REFRESHED(MASK_WEST_CENTER, STATE_INITIAL_EMPTY),

          /** The new group account has been added currently. */
          ADD_GROUP_ACCOUNT(MASK_WEST_CENTER, STATE_GROUP_ADD),

          /** The group has been edited currently. */
          EDIT_GROUP_ACCOUNT(MASK_WEST_CENTER, STATE_GROUP_EDIT),

          /** The single group has been selected. */
          SINGLE_GROUP_SELECTED(MASK_WEST_CENTER, STATE_GROUPS_SINGLE),

          /** The more than one group have been selected. */
          MULTI_GROUPS_SELECTED(MASK_WEST_CENTER, STATE_GROUPS_MULTI),

          /** The group and user have been selected. */
          GROUP_USER_SELECTED(MASK_CENTER, STATE_GROUPS_SINGLE_USER_SELECTED),

          /** The all user table has been filled. */
          USERS_LIST_POPULATED(MASK_EAST, STATE_ALL_USERS),

          /** The group users table has been refreshed. */
          GROUP_USERS_LIST_REFRESHED(MASK_CENTER_TOP, STATE_INITIAL_EMPTY);

          /** The current components states to be enabled or disabled */
          private final boolean[] state;

          /** The current mask that defines components to which the current state is applied */
          private final boolean[] mask;

          /**
           * Creates a target view state.
           *
           * @param    mask
           *           The current mask
           * @param    state
           *           The current state
           */
          private GroupsViewStates(boolean[] mask, boolean[] state)
          {
             this.mask  = mask;
             this.state = state;
          }

          /**
           * Gets the current state that defines the affected components states.
           *
           * @return   The current state
           */
          @Override
          public boolean[] getState()
          {
             return this.state;
          }

          /**
           * Gets the current mask that defines the set of affected components (widgets).
           *
           * @return   The current mask
           */
          @Override
          public boolean[] getMask()
          {
             return mask;
          }
       }
       /** state definition - initial empty */
       private static final boolean[] STATE_INITIAL_EMPTY =
       {
       /*       refresh,           */       true,
       /*       addGroup,          */       true,
       /*       deleteGroup,             */ false,
       /*       cloneGroup,              */ false,
       /*       editGroup,               */ false,
       /*       switchTOACLs,            */ false,

       /*       newUser,              */ false,
       /*       addUser,              */ false,
       /*       deleteUser,           */ false,

       /*        populateUsers,     */       true,
       /*        addSelected,             */ false,
       /*           filterSelector,       */ false,
       /*           filterUsers           */ false
       };

       /** state definition - group definition creation */
       private static final boolean[] STATE_GROUP_ADD =
       {
       /*       refresh,              */ false,
       /*       addGroup,             */ false,
       /*       deleteGroup,          */ false,
       /*       cloneGroup,           */ false,
       /*       editGroup,            */ false,
       /*       switchTOACLs,         */ false,

       /*       newUser,              */ false,
       /*       addUser,              */ false,
       /*       deleteUser,           */ false,

       /*        populateUsers,       */ false,
       /*        addSelected,         */ false,
       /*           filterSelector,   */ false,
       /*           filterUsers       */ false
       };

       /** state definition - group definition editing */
       private static final boolean[] STATE_GROUP_EDIT =
       {
       /*       refresh,              */ false,
       /*       addGroup,             */ false,
       /*       deleteGroup,          */ false,
       /*       cloneGroup,           */ false,
       /*       editGroup,            */ false,
       /*       switchTOACLs,         */ false,

       /*       newUser,              */ false,
       /*       addUser,              */ false,
       /*       deleteUser,           */ false,

       /*        populateUsers,          */ false,
       /*        addSelected,            */ false,
       /*           filterSelecto        */ false,
       /*           filterUsers          */ false
       };

       /** state definition - single selection in groups list */
       private static final boolean[] STATE_GROUPS_SINGLE =
       {
       /*       refresh,              */       true,
       /*       addGroup,             */       true,
       /*       deleteGroup,          */       true,
       /*       cloneGroup,           */       true,
       /*       editGroup,            */       true,
       /*       switchTOACLs,         */       true,

       /*       newUser,           */       true,
       /*       addUser,           */       true,
       /*       deleteUser,              */ false,

       /*        populateUsers,     */       true,
       /*        addSelected,             */ false,
       /*           filterSelector,       */ false,
       /*           filterUsers           */ false
       };

       /** A single user selected state*/
       private static final boolean[] STATE_GROUPS_SINGLE_USER_SELECTED =
       {
       /*       refresh,              */       true,
       /*       addGroup,             */       true,
       /*       deleteGroup,          */       true,
       /*       cloneGroup,           */       true,
       /*       editGroup,            */       true,
       /*       switchTOACLs,         */       true,

       /*       newUser,           */       true,
       /*       addUser,           */       true,
       /*       deleteUser,              */ true,

       /*        populateUsers,     */       true,
       /*        addSelected,             */ false,
       /*           filterSelector,       */ false,
       /*           filterUsers           */ false
       };

       /** state definition - multiple selection in groups list */
       private static final boolean[] STATE_GROUPS_MULTI =
       {
       /*       refresh,          */       true,
       /*       addGroup,         */       true,
       /*       deleteGroup,      */       true,
       /*       cloneGroup,             */ false,
       /*       editGroup,              */ false,
       /*       switchTOACLs,           */ false,

       /*       newUser,              */ false,
       /*       addUser,              */ false,
       /*       deleteUser,           */ false,

       /*        populateUsers,           */ true,
       /*        addSelected,             */ false,
       /*           filterSelecto         */ false,
       /*           filterUsers           */ false
       };

       /** state definition - all users list populated */
       private static final boolean[] STATE_ALL_USERS =
       {
       /*       refresh,              */ false,
       /*       addGroup,             */ false,
       /*       deleteGroup,          */ false,
       /*       cloneGroup,           */ false,
       /*       editGroup,            */ false,
       /*       switchTOACLs          */ false,

       /*       newUser,              */ false,
       /*       addUser,              */ false,
       /*       deleteUser,           */ false,

       /*        populateUsers,           */ true,
       /*        addSelected,             */ false,
       /*           filterSelector,      */  true,
       /*           filterUsers           */ true,
       };

       /** mask definition - west-center */
       private static final boolean[] MASK_WEST_CENTER =
       {
       /*       refresh,              */ true,
       /*       addGroup,             */ true,
       /*       deleteGroup,          */ true,
       /*       cloneGroup,           */ true,
       /*       editGroup,            */ true,
       /*       switchTOACLs          */ true,

       /*       newUser,              */ true,
       /*       addUser,              */ true,
       /*       deleteUser,           */ true,

       /*        populateUsers,       */ false,
       /*        addSelected,         */ false,
       /*           filterSelector,   */ false,
       /*           filterUsers       */ false
       };

       /** mask definition - center */
       private static final boolean[] MASK_CENTER =
       {
       /*       refresh,              */ false,
       /*       addGroup,             */ false,
       /*       deleteGroup,          */ false,
       /*       cloneGroup,           */ false,
       /*       editGroup,            */ false,
       /*       switchTOACLs          */ false,

       /*       newUser,              */ true,
       /*       addUser,              */ true,
       /*       deleteUser,           */ true,

       /*        populateUsers,       */ false,
       /*        addSelected,         */ false,
       /*           filterSelector,   */ false,
       /*           filterUsers       */ false
       };

       /** mask definition - center top */
       private static final boolean[] MASK_CENTER_TOP =
       {
       /*       refresh,              */ false,
       /*       addGroup,             */ false,
       /*       deleteGroup,          */ false,
       /*       cloneGroup,           */ false,
       /*       editGroup,            */ false,
       /*       switchTOACLs          */ false,

       /*       newUser,              */ false,
       /*       addUser,              */ false,
       /*       deleteUser,           */ true,

       /*        populateUsers,       */ false,
       /*        addSelected,         */ false,
       /*           filterSelector,   */ false,
       /*           filterUsers       */ false
       };

       /** mask definition - east */
       private static final boolean[] MASK_EAST =
       {
       /*       refresh,              */ false,
       /*       addGroup,             */ false,
       /*       deleteGroup,          */ false,
       /*       cloneGroup,           */ false,
       /*       editGroup,            */ false,
       /*       switchTOACLs,         */ false,

       /*       newUser,              */ false,
       /*       addUser,              */ false,
       /*       deleteUser,           */ false,

       /*        populateUsers,           */ true,
       /*        addSelected,             */ true,
       /*           filterSelector,       */ true,
       /*           filterUsers           */ true
       };

       /** The attachment point for modal dialogs on this view */
       public static final NestedSlot MODAL_CONTENT = new NestedSlot();

       /** Administration server interface */
       private final AdminServiceAsync adminService;

       /** The set of selected groups definitions */
       private final Set<GroupDef> selectedGroups;

       /** The currently selected group */
       private GroupDef selectedGroup;

       /** The common modal dialogs manager */
       private final ModalDialogs modalDialogsManager;

       /** The server alarms manager */
       private final Alarm serverAlarmsManager;

       /**
        * This view interface that must be implemented by the concrete view according to GWTP
        * framework. Defines the exposed business methods.
        */
       public interface MyView
       extends View, HasUiHandlers<GroupsUIHandlers>, Provider<HasEnabled[]>, PrintingContext
       {
          /**
           * Sets the all groups table model.
           *
           * @param    groups
           *           The array of groups given by tagged names
           */
          void setGroups(TaggedName[] groups);

          /**
           * Sets the group users table model.
           *
           * @param    users
           *           The array of group users given by tagged names
           */
          void setUsersInGroup(TaggedName[] users);

          /**
           * Sets all users model.
           *
           * @param    allUsers
           *           The array of all users accounts given by tagged names
           * @param    filter
           *           The search template to filter the displayed users
           */
          void setAllUsers(TaggedName[] allUsers, RegExp filter);

          /**
           * Tests if this group has been selected.
           *
           * @param    group
           *           The target group
           *
           * @return   True iff the target group has been selected, otherwise false.
           */
          boolean isSelectedGroup(TaggedName group);

          /**
           * Tests if these tested users have been selected.
           *
           * @param    users
           *           The array of tested user accounts given by tagged names
           *
           * @return   True if all tested users have been selected, otherwise false.
           */
          boolean isSelectedUsers(TaggedName[] users);

          /**
           * Select a group by its name.
           *
           * @param    groupName
           *           The group name
           */
          void selectGroupByName(String groupName);

          /**
           * Changes the Populate button's label from "Populate" to "Refresh"
           *
           * @param    populated
           *           The flag which true value means to change the Populate label to Refresh one
           */
          void togglePopulateUsersButton(boolean populated);

          /**
           * Gets a reference to the Group Definition View.
           *
           * @return   The Group Definition View
           */
          GroupDefinitionView getGroupDefinitionView();

          /**
           * Gets a reference to the Deleting Groups Alert View.
           *
           * @return   The Deleting Groups Alert View
           */
          DeletingGroupsAlert getDeletingGroupsAlert();

          /**
           * Gets a reference to the Clone Group View.
           *
           * @return   The Clone Group View
           */
          CloneGroupView getCloneGroupView();

          /**
           * Gets a reference to the Unassigning Users Alert View.
           *
           * @return   The Unassigning Users Alert View
           */
          UnassigningUsersAlert getUnassigningUsersAlert();
       }

       /**
        * Defines the proxy place for GWTP framework.
        */
       @ProxyStandard
       @NameToken(NameTokens.GROUPS)
       public interface MyProxy
       extends ProxyPlace<GroupsPresenter>
       {}

       /**
        * Defines the Groups presenter.
        *
        * @param    eventBus
        *           The event bus
        * @param    view
        *           The associated view
        * @param    proxy
        *           The proxy place
        * @param    placeManager
        *           The place manager
        * @param    viewStateMachine
        *           The view state machine
        * @param    adminService
        *           The administration service
        * @param    modalDialogsManager
        *           The common modal dialogs manager
        * @param    serverAlarmsManager
        *           The server alarms
        * @param    aclPopupPresenter
        *           The ACL popup presenter
        */
       @Inject
       public GroupsPresenter(EventBus eventBus,
                              MyView view,
                              MyProxy proxy,
                              PlaceManager placeManager,
                              @Named("GroupsViewStateMachine") ViewStateMachine viewStateMachine,
                              AdminServiceAsync adminService,
                              ModalDialogs modalDialogsManager,
                              Alarm serverAlarmsManager,
                              ACLPopupPresenter aclPopupPresenter)
       {
          super(eventBus,
                view,
                proxy,
                HomePresenter.SLOT_CONTENT,
                placeManager,
                viewStateMachine,
                aclPopupPresenter);

          this.adminService = adminService;

          this.modalDialogsManager = modalDialogsManager;

          this.serverAlarmsManager = serverAlarmsManager;

          this.selectedGroups = new HashSet<GroupDef>();

          getView().setUiHandlers(this);
       }

       /**
        * Loads the data before the associated view will be displayed.
        *
        * @param    request
        *           The current request
        */
       @Override
       public void prepareFromRequest(PlaceRequest request)
       {
          super.prepareFromRequest(request);
          updateCustomAuthPlugins();
          updateGroupsList(()->{setState(GroupsViewStates.GROUPS_LIST_REFRESHED);});
       }

       /**
        * Performs the user action to refresh the all groups table.
        */
       @Override
       public void onRefresh()
       {
          updateGroupsList(()->{changeState(GroupsViewStates.GROUPS_LIST_REFRESHED);});
       }

       /**
        * Requests the groups from the server asynchronously and then updates the All Groups grid with
        * actual values and executes the given command after getting the server response.
        *
        * @param    command
        *           The given command to execute
        */
       private void updateGroupsList(Runnable command)
       {
          this.adminService.listGroups(new AsyncCallback<TaggedName[]>()
          {

             @Override
             public void onSuccess(TaggedName[] result)
             {
                getView().setGroups(result);
                command.run();
             }

             @Override
             public void onFailure(Throwable caught)
             {
                logger.log(Level.SEVERE, "Failed to call listGroups", caught);
                command.run();
             }
          });
       }

       /**
        * Requests the users that belongs to the given group asynchronously and then updates the
        * Group Users grid with actual values after getting the server response.
        *
        * @param    group
        *           The group given by its tagged name
        */
       private void updateUsersInGroup(TaggedName group)
       {
          final String name = group.getName();
          this.adminService.listGroupUsers(name, new AsyncCallback<TaggedName[]>()
          {

             @Override
             public void onSuccess(TaggedName[] result)
             {
                getView().setUsersInGroup(result);
             }

             @Override
             public void onFailure(Throwable caught)
             {
                logger.log(Level.SEVERE, "Failed to call listGroupUsers for GROUP " + name, caught);
             }
          });
       }

       /**
        * Populates the all users table if it has been empty or updates its rows with the current data.
        */
       @Override
       public void populateAndRefreshAllUsers()
       {
          this.adminService.listUsers(new AsyncCallback<TaggedName[]>()
          {
             @Override
             public void onSuccess(TaggedName[] result)
             {
                getView().setAllUsers(result, filterExp);
                getView().togglePopulateUsersButton(true);
                if (result != null && result.length > 0)
                {
                   changeState(GroupsViewStates.USERS_LIST_POPULATED);
                }
             }

             @Override
             public void onFailure(Throwable caught)
             {
                logger.log(Level.SEVERE, "Failed to call listUsers", caught);
             }
          });
       }

       /**
        * Retrieves the custom authentication plugins data and fills the custom authentication plugins
        * combobox.
        */
       @Override
       public void updateCustomAuthPlugins()
       {
          this.adminService.listAuthPlugins(new AsyncCallback<TaggedName[]>()
          {
             @Override
             public void onSuccess(TaggedName[] result)
             {
                getView().getGroupDefinitionView().setCustomAuthPlugins(result);
             }

             @Override
             public void onFailure(Throwable caught)
             {
                logger.log(Level.SEVERE, "Failed to call listAuthPlugins", caught);
             }
          });
       }

       /**
        * Setups the Add Group Dialog.
        */
       @Override
       public void setupAddGroupDialog()
       {
          logger.log(Level.INFO, "setupAddGroupDialog");
          getView().getGroupDefinitionView().setUseCaseMode(UseCaseMode.ADD_GROUP, new GroupDef());
       }

       /**
        * Setups the Deleting Groups Alert dialog.
        *
        * @param    groups
        *           The array of the selected groups to delete
        */
       @Override
       public void setupDeletingGroupsAlert(TaggedName[] groups)
       {
          logger.log(Level.INFO, "setupDeleteSelectedGroupsDialog");
          getView().getDeletingGroupsAlert().setAlert(groups);
       }

       /**
        * Setups the Clone Group Dialog.
        *
        * @param    clonedGroup
        *           The target group to clone
        */
       @Override
       public void setupCloneGroupDialog(TaggedName clonedGroup)
       {
          logger.log(Level.INFO, "setupCloneGroupDialog");
          getView().getCloneGroupView().setupClonedGroup(clonedGroup);
       }

       /**
        * Setups the Edit Group Dialog.
        *
        * @param    updatedGroup
        *           The group definition to edit
        */
       @Override
       public void setupEditGroupDialog(TaggedName updatedGroup)
       {
          logger.log(Level.INFO, "setupEditGroupDialog");
          getView().getGroupDefinitionView().setUseCaseMode(UseCaseMode.EDIT_GROUP, selectedGroup);
       }

       /**
        * Cancels the Add Group Dialog.
        */
       @Override
       public void cancelAddGroupDialog()
       {
          logger.log(Level.INFO, "cancelAddGroupDialog");
          restoreState();
       }

       /**
        * Cancels the Deleting Groups Alert dialog.
        */
       @Override
       public void cancelDeletingGroupsAlert()
       {
          logger.log(Level.INFO, "cancelDeleteSelectedGroupsDialog");
       }

       /**
        * Cancels the Clone Group Dialog.
        */
       @Override
       public void cancelCloneGroupDialog()
       {
          logger.log(Level.INFO, "cancelCloneGroupDialog");
       }

       /**
        * Cancels the Edit Group Dialog.
        */
       @Override
       public void cancelEditGroupDialog()
       {
          logger.log(Level.INFO, "cancelEditGroupDialog");
          restoreState();
       }

       /**
        * Adds new group to the group accounts represented by the all groups table.
        *
        * @param    newGroup
        *           The new group definition object representing the new adding group
        */
       @Override
       public void addGroup(final GroupDef newGroup)
       {
          this.adminService.addGroup(newGroup, new AdminCallback<Boolean>(adminService,
                                                                          serverAlarmsManager)
          {
             @Override
             public void onDone(Boolean result)
             {
                updateGroupsList(()->{ getView().selectGroupByName(newGroup.subjectId); });
             }
          });
       }

       /**
        * Delete selected groups given by the array of group tagged names
        *
        * @param    groups
        *           The array of groups to delete
        */
       @Override
       public void deleteSelectedGroups(TaggedName[] groups)
       {
          for(TaggedName group : groups)
          {
             if (!getView().isSelectedGroup(group))
             {
                return;
             }
          }
          MultiCallback mc = new MultiCallback(adminService, serverAlarmsManager);
          LinkedHashMap<TaggedName, AdminCallback<Boolean>> callbacks = new LinkedHashMap<TaggedName, AdminCallback<Boolean>>();
          for(TaggedName group : groups)
          {
             AdminCallback<Boolean> callback = mc.newCallback();
             callbacks.put(group, callback);
             logger.info("group to delete " + group.getName());
          };
          for (Map.Entry<TaggedName, AdminCallback<Boolean>> e : callbacks.entrySet())
          {
             this.adminService.deleteGroup(e.getKey().getName(), e.getValue());
          }
          mc.onDone(() ->
          {
             if (mc.isSuccess())
             {
                updateGroupsList(()->{setState(GroupsViewStates.GROUPS_LIST_REFRESHED);});
             }
             else
             {
                for (Map.Entry<TaggedName, AdminCallback<Boolean>> e : callbacks.entrySet())
                {
                   if (e.getValue().getResult() == null || !e.getValue().getResult())
                   {
                      logger.info("failed to delete group " + e.getKey().getName());
                   }
                }
                updateGroupsList(()->{setState(GroupsViewStates.GROUPS_LIST_REFRESHED);});
             }
          });
       }

       /**
        * Removes the selected users from the given group.
        *
        * @param    group
        *           The group tagged name
        * @param    users
        *           The array of selected users given by their tagged names
        */
       @Override
       public void unassignSelectedUsers(final TaggedName group, TaggedName[] users)
       {
          if (!getView().isSelectedGroup(group) || !getView().isSelectedUsers(users))
          {
             return;
          }
          String[] selected = new String[users.length];
          for(int i = 0; i < users.length; i++)
          {
             selected[i] = users[i].getName();
          }
          this.adminService.changeGroupUsers(group.getName(), new String[0], selected,
                                             new AdminCallback<Boolean>(adminService,
                                                                        serverAlarmsManager)
          {
             @Override
             public void onDone(Boolean result)
             {
                updateUsersInGroup(group);
                restoreState();
             }
          });
       }

       /**
        * Setups the Unassigning Users Alert dialog.
        *
        * @param    group
        *           The group tagged name
        * @param    users
        *           The array of selected users given by their tagged names
        */
       @Override
       public void setupUnassigningUsersAlert(TaggedName group, TaggedName[] users)
       {
          getView().getUnassigningUsersAlert().setAlert(group, users);
       }

       /**
        * Clones the given group by creating the new one.
        *
        * @param    nameFrom
        *           The name of the group to clone
        * @param    nameTo
        *           The name for the new cloned group
        * @param    acls
        *           True indicates that the ACL of target group will be cloned too, otherwise
        *           the ACL of the new created group will be empty.
        */
       @Override
       public void cloneGroup(final String nameFrom, String nameTo, boolean acls)
       {
          this.adminService.cloneGroup(nameFrom, nameTo, acls,
                                       new AdminCallback<Boolean>(adminService, serverAlarmsManager)
          {
             @Override
             public void onDone(Boolean result)
             {
                logger.log(Level.INFO, "cloneGroup " + nameFrom + ", " + result);
                updateGroupsList(()->{changeState(GroupsViewStates.GROUPS_LIST_REFRESHED);});
             }
          });
       }

       /**
        * Commits changes in the target group definition.
        *
        * @param    updatedGroup
        *           The group definition to commit its changes
        */
       @Override
       public void updateGroup(final GroupDef updatedGroup)
       {
          this.adminService.setGroup(updatedGroup,
                                     new AdminCallback<Boolean>(adminService, serverAlarmsManager)
          {
             @Override
             public void onDone(Boolean result)
             {
                logger.log(Level.INFO, "setGroup " + updatedGroup.subjectId + ", " + result);
                updateGroupsList(()->{getView().selectGroupByName(updatedGroup.subjectId);});
             }
          });
       }

       /**
        * Triggers the groups selection changed.
        *
        * @param    groups
        *           The new set of selected groups
        */
       @Override
       public void onGroupSelected(Set<TaggedName> groups)
       {
          if (groups == null)
          {
             logger.info("Groups selection is cleared.");
             changeState(GroupsViewStates.GROUPS_LIST_REFRESHED);
             return;
          }
          MultiCallback mc = new MultiCallback(adminService, serverAlarmsManager);
          LinkedHashMap<TaggedName, AdminCallback<GroupDef>> callbacks = new LinkedHashMap<TaggedName, AdminCallback<GroupDef>>();
          groups.forEach(group ->
          {
             AdminCallback<GroupDef> cb = mc.newCallback();
             callbacks.put(group, cb);
             logger.info("get group " + group.getName());
          });
          for (Map.Entry<TaggedName, AdminCallback<GroupDef>> e : callbacks.entrySet())
          {
             this.adminService.getGroup(e.getKey().getName(), e.getValue());
          }
          mc.onDone(() ->
          {
             if (mc.isSuccess())
             {
                selectedGroups.clear();
                for (Map.Entry<TaggedName, AdminCallback<GroupDef>> e : callbacks.entrySet())
                {
                   GroupDef groupDef = e.getValue().getResult();
                   logger.info("selected group " + groupDef);
                   selectedGroups.add(groupDef);
                   selectedGroup = groupDef;
                }
                if (selectedGroups.size() > 1)
                {
                   changeState(GroupsViewStates.MULTI_GROUPS_SELECTED);
                }
                else if (selectedGroups.size() == 1)
                {
                   changeState(GroupsViewStates.SINGLE_GROUP_SELECTED);
                }
                else
                {
                   changeState(GroupsViewStates.GROUPS_LIST_REFRESHED);
                }
             }
             else
             {
                for (Map.Entry<TaggedName, AdminCallback<GroupDef>> e : callbacks.entrySet())
                {
                   if (e.getValue().getResult() == null)
                   {
                      logger.info("Can't get group definition for " + e.getKey().getName());
                   }
                }
                changeState(GroupsViewStates.GROUPS_LIST_REFRESHED);
             }
          });

          if (callbacks.size() == 1)
          {
             TaggedName groupTaggedName = callbacks.keySet().toArray(new TaggedName [1])[0];
             updateUsersInGroup(groupTaggedName);
          }
          else
          {
             getView().setUsersInGroup(null);
          }

       }

       /**
        * Triggers the users selection changed.
        *
        * @param    selectedUsers
        *           The new set of selected users
        */
       @Override
       public void onUserSelected(Set<TaggedName> selectedUsers)
       {
          if (selectedUsers == null || selectedUsers.isEmpty())
          {
             changeState(GroupsViewStates.GROUP_USERS_LIST_REFRESHED);

          }
          else
          {
             changeState(GroupsViewStates.GROUP_USER_SELECTED);
          }
       }

       /**
        * Defines the method that is invoked if the Unassigning Users Alert has been cancelled.
        */
       @Override
       public void cancelUnassigningUsersAlert()
       {}

       /**
        * Adds a user to the given group.
        *
        * @param    group
        *           The group tagged name
        * @param    user
        *           The user account name
        */
       @Override
       public void addUserToGroup(TaggedName group, String user)
       {
          if (!getView().isSelectedGroup(group))
          {
             return;
          }

          this.adminService.changeGroupUsers(group.getName(), new String[] {user}, new String[0],
                                             new AdminCallback<Boolean>(adminService,
                                                                        serverAlarmsManager)
          {
             @Override
             public void onDone(Boolean result)
             {
                updateUsersInGroup(group);
             }
          });
       }

       /**
        * Sets the target user search template.
        *
        * @param    value
        *           The target search template
        */
       @Override
       public void setUsersFilter(String value)
       {
          this.setFilter(value);

          populateAndRefreshAllUsers();
       }

       /**
        * Adds the selected users to the given group.
        *
        * @param    group
        *           The group tagged name
        * @param    selectedUsers
        *           The array of selected users given by their tagged names
        */
       @Override
       public void addUsersToGroup(TaggedName group, Set<TaggedName> selectedUsers)
       {
          if (!getView().isSelectedGroup(group))
          {
             //TODO
             return;
          }
          int size = selectedUsers.size();
          ArrayList<String> usersList = new ArrayList<String>(size);
          selectedUsers.forEach(user -> usersList.add(user.getName()));
          this.adminService.changeGroupUsers(group.getName(),
                                             usersList.toArray(new String[size]),
                                             new String[0],
                                             new AdminCallback<Boolean>(adminService,
                                                                        serverAlarmsManager)
          {
             @Override
             public void onDone(Boolean result)
             {
                updateUsersInGroup(group);
             }
          });
       }
    }