/* * ARX: Powerful Data Anonymization * Copyright 2012 - 2018 Fabian Prasser and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.deidentifier.arx.gui.view.impl.common; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.math3.util.Pair; import org.deidentifier.arx.gui.Controller; import org.deidentifier.arx.gui.resources.Resources; import org.deidentifier.arx.gui.view.SWTUtil; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.nebula.widgets.pagination.IPageLoader; import org.eclipse.nebula.widgets.pagination.PageableController; import org.eclipse.nebula.widgets.pagination.collections.PageListHelper; import org.eclipse.nebula.widgets.pagination.collections.PageResult; import org.eclipse.nebula.widgets.pagination.table.PageableTable; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; /** * This class implements a table, in which properties can be filtered. * * @author Fabian Prasser */ public class ComponentFilterTable { /** * Page loader * @author Fabian Prasser */ private class FilterPageLoader implements IPageLoader<PageResult<Pair<String, Set<String>>>> { @Override public PageResult<Pair<String, Set<String>>> loadPage(PageableController controller) { if (keys == null || properties == null || keyProperties == null) { return PageListHelper.createPage(new ArrayList<Pair<String, Set<String>>>(), controller); } else { List<Pair<String, Set<String>>> list = new ArrayList<>(); for (String key : keys) { list.add(new Pair<String, Set<String>>(key, keyProperties.get(key))); } return PageListHelper.createPage(list, controller); } } } /** Image. */ private final Image IMAGE_ENABLED; /** Image. */ private final Image IMAGE_DISABLED; /** Widget. */ private final PageableTable pageableTable; /** Widget. */ private final Table table; /** State*/ private List<String> keys; /** The registered listeners. */ private List<SelectionListener> listeners; /** The list of properties. */ private Map<String, Set<String>> keyProperties; /** The list of properties. */ private List<String> properties; /** State*/ private String selectedKey; /** State*/ private String selectedProperty; /** State*/ private Map<String, Set<String>> permitted; /** * Creates a new instance. * * @param parent * @param controller * @param properties * @param entries */ public ComponentFilterTable(Composite parent, Controller controller) { IMAGE_ENABLED = controller.getResources().getManagedImage("tick.png"); //$NON-NLS-1$ IMAGE_DISABLED = controller.getResources().getManagedImage("cross.png"); //$NON-NLS-1$ this.listeners = new ArrayList<SelectionListener>(); this.pageableTable = SWTUtil.createPageableTableViewer(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL, false, false); this.pageableTable.getViewer().setContentProvider(new ArrayContentProvider()); this.pageableTable.setPageLoader(new FilterPageLoader()); this.pageableTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); this.table = this.pageableTable.getViewer().getTable(); this.table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); this.table.setHeaderVisible(true); // React on events this.table.addMouseListener(new MouseAdapter(){ @Override @SuppressWarnings("unchecked") public void mouseDown(MouseEvent arg0) { int row = getItemRowAt(arg0.x, arg0.y); int column = getItemColumnAt(arg0.x, arg0.y); if (row != -1 && column > 0 && column <= ComponentFilterTable.this.properties.size()) { String property = ComponentFilterTable.this.properties.get(column-1); String entry = ((Pair<String, Set<String>>)ComponentFilterTable.this.table.getItem(row).getData()).getFirst(); if (permitted != null && permitted.get(entry).contains(property) && property != null && entry != null) { boolean selected = isSelected(entry, property); setSelected(entry, property, !selected); fireSelectionEvent(entry, property); } } else { selectedProperty = null; selectedKey = null; } } }); // Init this.pageableTable.setCurrentPage(0); this.pageableTable.refreshPage(); } /** * Adds a selection listener. * * @param listener */ public void addSelectionListener(SelectionListener listener) { this.listeners.add(listener); } /** * Clears the table. */ public void clear() { this.selectedKey = null; this.keys = null; this.selectedProperty = null; this.keyProperties = null; this.properties = null; this.permitted = null; this.pageableTable.setRedraw(false); for (TableColumn c : this.pageableTable.getViewer().getTable().getColumns()) { c.dispose(); } this.pageableTable.setRedraw(true); } /** * Returns the entries. * @param entry * @return */ public int getIndexOfEntry(String entry) { return this.keys.indexOf(entry); } /** * Returns the properties. * * @return */ public List<String> getProperties() { return properties; } /** * Returns the currently selected entry. * * @return */ public String getSelectedEntry() { return selectedKey; } /** * Returns the currently selected property. * * @return */ public String getSelectedProperty() { return selectedProperty; } /** * Returns whether the given property is selected for the given entry. * * @param entry * @param property * @return */ public boolean isSelected(String entry, String property) { if (!this.keyProperties.containsKey(entry)) { throw new RuntimeException(Resources.getMessage("ComponentFilterTable.3")); //$NON-NLS-1$ } if (!this.properties.contains(property)) { throw new RuntimeException(Resources.getMessage("ComponentFilterTable.4")); //$NON-NLS-1$ } return this.keyProperties.get(entry).contains(property); } /** * Enable/disable. * * @param enabled */ public void setEnabled(boolean enabled) { this.pageableTable.setEnabled(enabled); } /** * Sets layout data. * * @param layoutData */ public void setLayoutData(Object layoutData) { this.pageableTable.setLayoutData(layoutData); } /** * Specifies the properties permitted per entry * @param entries * @param permitted */ public void setPermitted(List<String> entries, Map<String, Set<String>> permitted) { // Store this.keys = entries; this.keyProperties = new HashMap<>(permitted); // Create deep copy of for managing permitted values this.permitted = new HashMap<>(); for (Entry<String, Set<String>> entry : keyProperties.entrySet()) { this.permitted.put(entry.getKey(), new HashSet<>(entry.getValue())); } // Refresh table this.pageableTable.refreshPage(); this.pageableTable.setCurrentPage(0); } /** * Sets new properties. Clears the table * @param properties */ public void setProperties(List<String> properties) { // Clear this.clear(); this.properties = new ArrayList<String>(properties); /* First column for attribute names*/ TableViewerColumn column = new TableViewerColumn(pageableTable.getViewer(), SWT.NONE); column.setLabelProvider(new ColumnLabelProvider() { @Override public Image getImage(Object element) { return null; } @Override @SuppressWarnings("unchecked") public String getText(Object element) { return ((Pair<String, Set<String>>)element).getKey(); } }); TableColumn tColumn = column.getColumn(); tColumn.setText(Resources.getMessage("ComponentFilterTable.9")); //$NON-NLS-1$ tColumn.setToolTipText(Resources.getMessage("ComponentFilterTable.9")); //$NON-NLS-1$ tColumn.setWidth(150); // One column per property for (final String property : properties) { /* Empty column to make checkboxes appear in an own cell */ column = new TableViewerColumn(pageableTable.getViewer(), SWT.NONE); column.setLabelProvider(new ColumnLabelProvider() { @Override @SuppressWarnings("unchecked") public Image getImage(Object element) { String key = ((Pair<String, Set<String>>)element).getKey(); Set<String> values = ((Pair<String, Set<String>>)element).getValue(); if (ComponentFilterTable.this.permitted == null) { return null; } else if (!ComponentFilterTable.this.permitted.get(key).contains(property)) { return null; } else if (values.contains(property)) { return IMAGE_ENABLED; } else { return IMAGE_DISABLED; } } @Override public String getText(Object element) { return ""; } }); tColumn = column.getColumn(); tColumn.setText(property); tColumn.setToolTipText(property); tColumn.setWidth(30); } } /** * Sets the given property selected for the given entry. * * @param entry * @param property * @param selected */ public void setSelected(String entry, String property, boolean selected) { // Check if (this.properties == null || this.keyProperties == null) { return; } // We only support this for existing entries and properties if (!this.properties.contains(property)) { return; } if (!this.keyProperties.containsKey(entry)) { return; } if (selected) { this.keyProperties.get(entry).add(property); } else { this.keyProperties.get(entry).remove(property); } this.pageableTable.refreshPage(); } /** * Fires a new event * @param selectedEntry * @param selectedProperty */ private void fireSelectionEvent(String selectedEntry, String selectedProperty) { // Maintain state this.selectedKey = selectedEntry; this.selectedProperty = selectedProperty; // Fire event Event event = new Event(); event.display = pageableTable.getDisplay(); event.item = pageableTable; event.widget = pageableTable; SelectionEvent sEvent = new SelectionEvent(event); for (SelectionListener listener : listeners) { listener.widgetSelected(sEvent); } } /** * Returns the item at the given location. * * @param x * @param y * @return */ private int getItemColumnAt(int x, int y) { Point pt = new Point(x, y); int index = table.getTopIndex(); while (index < table.getItemCount()) { final TableItem item = table.getItem(index); for (int i = 0; i < table.getColumns().length; i++) { final Rectangle rect = item.getBounds(i); if (rect.contains(pt)) { return i; } } index++; } return -1; } /** * Returns the item at the given location. * * @param x * @param y * @return */ private int getItemRowAt(int x, int y) { Point pt = new Point(x, y); int index = table.getTopIndex(); while (index < table.getItemCount()) { final TableItem item = table.getItem(index); for (int i = 0; i < table.getColumns().length; i++) { final Rectangle rect = item.getBounds(i); if (rect.contains(pt)) { return index; } } index++; } return -1; } }