/*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.jboss.as.console.client.administration.role.form;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SelectionChangeEvent;
import org.jboss.as.console.client.Console;
import org.jboss.as.console.client.administration.role.model.Role;
import org.jboss.as.console.client.administration.role.model.RoleComparator;
import org.jboss.as.console.client.administration.role.model.Roles;
import org.jboss.as.console.client.widgets.lists.DefaultCellList;
import org.jboss.ballroom.client.widgets.forms.FormItem;
import org.jboss.ballroom.client.widgets.tables.DefaultPager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.jboss.as.console.client.administration.role.form.IncludeExcludeFormItem.Type.*;
/**
* Form item for the include and exclude roles of an {@link org.jboss.as.console.client.administration.role.model.RoleAssignment}.
*
* @author Harald Pehl
*/
public class IncludeExcludeFormItem extends FormItem<Map<IncludeExcludeFormItem.Type, Set<Role>>> {
private final static Templates TEMPLATES = GWT.create(Templates.class);
private final Map<Type, Set<Role>> value;
private final ProvidesKey<Role> keyProvider;
private final ListDataProvider<Role> availableProvider;
private final ListDataProvider<Role> includeProvider;
private final ListDataProvider<Role> excludeProvider;
private final MultiSelectionModel<Role> availableSelectionModel;
private final MultiSelectionModel<Role> includeSelectionModel;
private final MultiSelectionModel<Role> excludeSelectionModel;
private Button addInclude;
private Button removeInclude;
private Button addExclude;
private Button removeExclude;
private FormItemPanelWrapper wrapper;
private Roles roles;
public IncludeExcludeFormItem(final String name, final String title) {
super(name, title);
value = new HashMap<Type, Set<Role>>();
value.put(AVAILABLE, new HashSet<Role>());
value.put(INCLUDE, new HashSet<Role>());
value.put(EXCLUDE, new HashSet<Role>());
keyProvider = new Role.Key();
// available roles
availableProvider = new ListDataProvider<Role>(keyProvider);
availableSelectionModel = new MultiSelectionModel<Role>(keyProvider);
// included (assigned) roles
includeProvider = new ListDataProvider<Role>(keyProvider);
includeSelectionModel = new MultiSelectionModel<Role>(keyProvider);
// excluded roles
excludeProvider = new ListDataProvider<Role>(keyProvider);
excludeSelectionModel = new MultiSelectionModel<Role>(keyProvider);
}
@Override
@SuppressWarnings("unchecked")
public Widget asWidget() {
// available roles and pager
CellList<Role> availableList = new DefaultCellList<Role>(new RoleCell(), keyProvider);
availableList.setPageSize(12);
availableList.addStyleName("roles-list");
availableList.addStyleName("roles-list-available");
availableList.setSelectionModel(availableSelectionModel);
availableProvider.addDataDisplay(availableList);
availableSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@Override
public void onSelectionChange(final SelectionChangeEvent event) {
onSelect(AVAILABLE);
}
});
DefaultPager pager = new DefaultPager();
pager.setWidth("auto");
pager.setDisplay(availableList);
// included (assigned) roles
CellList<Role> includeList = new DefaultCellList<Role>(new RoleCell(), keyProvider);
includeList.setSelectionModel(includeSelectionModel);
includeProvider.addDataDisplay(includeList);
includeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@Override
public void onSelectionChange(final SelectionChangeEvent event) {
onSelect(INCLUDE);
}
});
ScrollPanel includeScroller = new ScrollPanel(includeList);
includeScroller.addStyleName("roles-list");
includeScroller.addStyleName("roles-list-assigned");
// excluded roles
CellList<Role> excludeList = new DefaultCellList<Role>(new RoleCell(), keyProvider);
excludeList.setSelectionModel(excludeSelectionModel);
excludeProvider.addDataDisplay(excludeList);
excludeSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@Override
public void onSelectionChange(final SelectionChangeEvent event) {
onSelect(EXCLUDE);
}
});
ScrollPanel excludeScroller = new ScrollPanel(excludeList);
excludeScroller.addStyleName("roles-list");
excludeScroller.addStyleName("roles-list-excluded");
// add / remove buttons
addInclude = new Button(TEMPLATES.arrow("right"));
addInclude.getElement().setId("role_assignment_add_include");
addInclude.setEnabled(false);
addInclude.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
onAdd(INCLUDE);
}
});
removeInclude = new Button(TEMPLATES.arrow("left"));
removeInclude.getElement().setId("role_assignment_remove_include");
removeInclude.setEnabled(false);
removeInclude.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
onRemove(INCLUDE);
}
});
addExclude = new Button(TEMPLATES.arrow("right"));
addExclude.getElement().setId("role_assignment_add_exclude");
addExclude.setEnabled(false);
addExclude.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
onAdd(EXCLUDE);
}
});
removeExclude = new Button(TEMPLATES.arrow("left"));
removeExclude.getElement().setId("role_assignment_remove_exclude");
removeExclude.setEnabled(false);
removeExclude.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
onRemove(EXCLUDE);
}
});
// put everything into panels
HorizontalPanel content = new HorizontalPanel();
content.setWidth("95%");
// left: available
Label label = new Label(Console.CONSTANTS.administration_available_roles());
label.addStyleName("roles-list-label");
VerticalPanel availableTitleAndTable = vert(label, availableList, pager);
DOM.setStyleAttribute(availableTitleAndTable.getElement(), "marginBottom", "20px");
content.add(availableTitleAndTable);
availableTitleAndTable.getElement().getParentElement().setAttribute("width", "45%");
// right - top: buttons and includes
HorizontalPanel rightTop = new HorizontalPanel();
label = new Label(Console.CONSTANTS.administration_assigned_roles());
label.addStyleName("roles-list-label");
VerticalPanel includeTitleAndTable = vert(label, includeScroller);
DOM.setStyleAttribute(includeTitleAndTable.getElement(), "marginBottom", "20px");
VerticalPanel includeButtons = vert(addInclude, removeInclude);
DOM.setStyleAttribute(includeButtons.getElement(), "margin", "5px");
rightTop.add(includeButtons);
includeButtons.getElement().getParentElement().setAttribute("width", "10%");
includeButtons.getElement().getParentElement().setAttribute("style", "vertical-align: middle");
rightTop.add(includeTitleAndTable);
// right - bottom: buttons and excludes
HorizontalPanel rightBottom = new HorizontalPanel();
label = new Label(Console.CONSTANTS.administration_excluded_roles());
label.addStyleName("roles-list-label");
VerticalPanel excludeTitleAndTable = vert(label, excludeScroller);
DOM.setStyleAttribute(excludeTitleAndTable.getElement(), "marginBottom", "20px");
VerticalPanel excludeButtons = vert(addExclude, removeExclude);
DOM.setStyleAttribute(excludeButtons.getElement(), "margin", "5px");
rightBottom.add(excludeButtons);
excludeButtons.getElement().getParentElement().setAttribute("width", "10%");
excludeButtons.getElement().getParentElement().setAttribute("style", "vertical-align: middle");
rightBottom.add(excludeTitleAndTable);
// right: include and exclude
VerticalPanel right = new VerticalPanel();
right.add(rightTop);
right.add(rightBottom);
content.add(right);
right.getElement().getParentElement().setAttribute("width", "50%");
wrapper = new FormItemPanelWrapper(content, this);
return wrapper;
}
private VerticalPanel vert(Widget... widgets) {
VerticalPanel panel = new VerticalPanel();
if (widgets != null) {
for (Widget widget : widgets) {
panel.add(widget);
}
}
return panel;
}
private void onSelect(final Type type) {
switch (type) {
case AVAILABLE:
boolean availableSelected = !availableSelectionModel.getSelectedSet().isEmpty();
addInclude.setEnabled(availableSelected);
addExclude.setEnabled(availableSelected);
break;
case INCLUDE:
removeInclude.setEnabled(!includeSelectionModel.getSelectedSet().isEmpty());
break;
case EXCLUDE:
removeExclude.setEnabled(!excludeSelectionModel.getSelectedSet().isEmpty());
break;
}
}
private void onAdd(final Type type) {
switch (type) {
case INCLUDE: {
Set<Role> selectedSet = availableSelectionModel.getSelectedSet();
if (!selectedSet.isEmpty()) {
value.get(AVAILABLE).removeAll(selectedSet);
value.get(INCLUDE).addAll(selectedSet);
availableSelectionModel.clear();
updateDataProvider();
}
break;
}
case EXCLUDE: {
Set<Role> selectedSet = availableSelectionModel.getSelectedSet();
if (!selectedSet.isEmpty()) {
value.get(AVAILABLE).removeAll(selectedSet);
value.get(EXCLUDE).addAll(selectedSet);
availableSelectionModel.clear();
updateDataProvider();
}
break;
}
}
}
private void onRemove(final Type type) {
switch (type) {
case INCLUDE: {
Set<Role> selectedSet = includeSelectionModel.getSelectedSet();
if (!selectedSet.isEmpty()) {
value.get(INCLUDE).removeAll(selectedSet);
value.get(AVAILABLE).addAll(selectedSet);
includeSelectionModel.clear();
updateDataProvider();
}
break;
}
case EXCLUDE: {
Set<Role> selectedSet = excludeSelectionModel.getSelectedSet();
if (!selectedSet.isEmpty()) {
value.get(EXCLUDE).removeAll(selectedSet);
value.get(AVAILABLE).addAll(selectedSet);
excludeSelectionModel.clear();
updateDataProvider();
}
break;
}
}
}
@Override
public void setEnabled(final boolean b) {
}
@Override
public boolean validate(final Map<Type, Set<Role>> value) {
//noinspection SimplifiableIfStatement
if (isRequired) {
return value != null && (!value.get(INCLUDE).isEmpty() || !value.get(EXCLUDE).isEmpty());
}
return true;
}
@Override
public void clearValue() {
clearSelection();
updateDataProvider();
}
@Override
public Map<Type, Set<Role>> getValue() {
return this.value;
}
@Override
public void setValue(final Map<Type, Set<Role>> value) {
Set<Role> includes = value.get(INCLUDE);
if (includes != null) {
this.value.get(INCLUDE).clear();
this.value.get(INCLUDE).addAll(includes);
this.value.get(AVAILABLE).removeAll(this.value.get(INCLUDE));
}
Set<Role> excludes = value.get(EXCLUDE);
if (excludes != null) {
this.value.get(EXCLUDE).clear();
this.value.get(EXCLUDE).addAll(excludes);
this.value.get(AVAILABLE).removeAll(this.value.get(EXCLUDE));
}
if (roles != null) {
Set<Role> available = new HashSet<Role>(roles.getRoles());
if (includes != null) {available.removeAll(includes);}
if (excludes != null) {available.removeAll(excludes);}
this.value.put(AVAILABLE, available);
}
clearSelection();
updateDataProvider();
}
@Override
public String asString() {
RoleComparator comperator = new RoleComparator();
List<Role> includes = new ArrayList<Role>(value.get(INCLUDE));
Collections.sort(includes, comperator);
StringBuilder builder = new StringBuilder().append(rolesCsv(includes, ""));
if (builder.length() > 0 && !value.get(EXCLUDE).isEmpty()) {
List<Role> excludes = new ArrayList<Role>(value.get(EXCLUDE));
Collections.sort(excludes, comperator);
builder.append(", ").append(rolesCsv(excludes, "-"));
}
return builder.toString();
}
private String rolesCsv(Collection<Role> roles, String prefix) {
StringBuilder builder = new StringBuilder();
for (Iterator<Role> iterator = roles.iterator(); iterator.hasNext(); ) {
Role role = iterator.next();
builder.append(prefix).append(role.getName());
if (role.isScoped()) {
builder.append(" ").append(role.getScope());
}
if (iterator.hasNext()) {
builder.append(", ");
}
}
return builder.toString();
}
@Override
public void setErroneous(boolean b) {
super.setErroneous(b);
wrapper.setErroneous(b);
}
@Override
public String getErrMessage() {
return super.getErrMessage() + ": Assign or exclude at least one role.";
}
public void update(final Roles roles) {
this.roles = roles;
value.get(AVAILABLE).clear();
value.get(AVAILABLE).addAll(roles.getRoles());
updateDataProvider();
}
private void clearSelection() {
availableSelectionModel.clear();
includeSelectionModel.clear();
excludeSelectionModel.clear();
}
private void updateDataProvider() {
ArrayList<Role> availableList = new ArrayList<Role>(value.get(AVAILABLE));
ArrayList<Role> includeList = new ArrayList<Role>(value.get(INCLUDE));
ArrayList<Role> excludeList = new ArrayList<Role>(value.get(EXCLUDE));
RoleComparator comparator = new RoleComparator();
Collections.sort(availableList, comparator);
Collections.sort(includeList, comparator);
Collections.sort(excludeList, comparator);
availableProvider.setList(availableList);
includeProvider.setList(includeList);
excludeProvider.setList(excludeList);
setModified(true);
}
public static enum Type {
AVAILABLE, INCLUDE, EXCLUDE
}
interface Templates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<i class=\"icon-arrow-{0}\"></i>")
SafeHtml arrow(String direction);
}
static class RoleCell extends AbstractCell<Role> {
@Override
public void render(final Context context, final Role value, final SafeHtmlBuilder sb) {
sb.appendEscaped(value.getName());
}
}
}