/*
* ARX: Powerful Data Anonymization
* Copyright 2012 - 2017 Fabian Prasser, Florian Kohlmayer 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.define;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.deidentifier.arx.AttributeType;
import org.deidentifier.arx.DataHandle;
import org.deidentifier.arx.DataType;
import org.deidentifier.arx.DataType.ARXOrderedString;
import org.deidentifier.arx.DataType.DataTypeDescription;
import org.deidentifier.arx.DataType.DataTypeWithFormat;
import org.deidentifier.arx.gui.Controller;
import org.deidentifier.arx.gui.model.Model;
import org.deidentifier.arx.gui.model.ModelEvent;
import org.deidentifier.arx.gui.model.ModelEvent.ModelPart;
import org.deidentifier.arx.gui.resources.Resources;
import org.deidentifier.arx.gui.view.SWTUtil;
import org.deidentifier.arx.gui.view.def.IView;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.TableItem;
import de.linearbits.swt.table.DynamicTable;
import de.linearbits.swt.table.DynamicTableColumn;
/**
* This view lists all attributes and their metadata
*
* @author Fabian Prasser
* @author Martin Waltl
*/
public class ViewAttributeList implements IView {
/** Controller */
private final Controller controller;
/** Model */
private Model model;
/** Model */
private String[] dataTypes;
/** View */
private DynamicTable table;
/**
* Creates a new instance.
*
* @param parent
* @param controller
* @param layoutCriteria
*/
public ViewAttributeList(final Composite parent,
final Controller controller) {
// Controller
this.controller = controller;
this.controller.addListener(ModelPart.INPUT, this);
this.controller.addListener(ModelPart.MODEL, this);
this.controller.addListener(ModelPart.SELECTED_ATTRIBUTE, this);
this.controller.addListener(ModelPart.ATTRIBUTE_TYPE, this);
this.controller.addListener(ModelPart.DATA_TYPE, this);
this.dataTypes = getDataTypes();
// Create group
this.create(parent);
this.reset();
}
@Override
public void dispose() {
controller.removeListener(this);
}
@Override
public void reset() {
table.clearAll();
SWTUtil.disable(table);
}
@Override
public void update(final ModelEvent event) {
if (event.part == ModelPart.MODEL) {
this.model = (Model) event.data;
updateEntries();
} else if (event.part == ModelPart.INPUT) {
updateEntries();
} else if (event.part == ModelPart.SELECTED_ATTRIBUTE) {
String attribute = (String) event.data;
updateSelectedAttribute(attribute);
} else if (event.part == ModelPart.ATTRIBUTE_TYPE) {
updateAttributeTypes();
} else if (event.part == ModelPart.DATA_TYPE) {
updateDataTypes();
}
}
/**
* Data type changed
*/
private void actionDataTypeChanged(String label) {
if (label != null && model != null && model.getInputConfig().getInput() != null) {
// Prepare
String attribute = model.getSelectedAttribute();
DataTypeDescription<?> description = getDataTypeDescription(label);
DataType<?> type = model.getInputDefinition().getDataType(attribute);
boolean changed = false;
try {
// Open format dialog
if (description.getLabel().equals("Ordinal")) { //$NON-NLS-1$
final String text1 = Resources.getMessage("AttributeDefinitionView.9"); //$NON-NLS-1$
final String text2 = Resources.getMessage("AttributeDefinitionView.10"); //$NON-NLS-1$
String[] array = controller.actionShowOrderValuesDialog(controller.getResources().getShell(),
text1,
text2,
type,
model.getLocale(),
getValuesAsArray(attribute));
// Only update the data type of the attribute if an order has been determined
if (array != null) {
// Only update the data type of the attribute if the selected type is valid
DataType<?> tempType = DataType.createOrderedString(array);
if (isValidDataType(tempType, getValuesAsList(attribute))) {
type = tempType;
changed = true;
}
}
} else if (description.hasFormat()) {
final String text1 = Resources.getMessage("AttributeDefinitionView.9"); //$NON-NLS-1$
final String text2 = Resources.getMessage("AttributeDefinitionView.10"); //$NON-NLS-1$
final String format = controller.actionShowFormatInputDialog(controller.getResources().getShell(),
text1,
text2,
model.getLocale(),
description,
getValuesAsList(attribute));
// Only update the data type of the attribute if a format has been selected
if (format != null) {
// The format input already performs a validity check,
// hence the returned format is valid
type = description.newInstance(format, model.getLocale());
changed = true;
}
} else {
// Only update the data type of the attribute if the selected type is valid
DataType<?> typeTemp = description.newInstance();
if (isValidDataType(typeTemp, getValuesAsList(attribute))) {
type = typeTemp;
changed = true;
}
}
// Handle unexpected errors
} catch (Exception e) {
controller.actionShowInfoDialog(controller.getResources().getShell(),
Resources.getMessage("ViewAttributeDefinition.1"), Resources.getMessage("ViewAttributeDefinition.2") + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
type = DataType.STRING;
changed = true;
}
// Set and update
if (changed) {
this.model.getInputDefinition().setDataType(attribute, type);
this.updateDataTypes();
this.controller.update(new ModelEvent(this, ModelPart.DATA_TYPE, attribute));
}
}
}
/**
* Creates the required controls.
*
* @param parent
*/
private void create(final Composite parent) {
this.table = SWTUtil.createTableDynamic(parent, SWT.SINGLE | SWT.V_SCROLL | SWT.FULL_SELECTION);
this.table.setHeaderVisible(true);
this.table.setLinesVisible(true);
this.table.setLayoutData(SWTUtil.createFillGridData());
SWTUtil.createGenericTooltip(table);
DynamicTableColumn column0 = new DynamicTableColumn(table, SWT.NONE);
column0.setText(""); //$NON-NLS-1$
column0.setWidth("4%", "25px"); //$NON-NLS-1$ //$NON-NLS-2$
DynamicTableColumn column1 = new DynamicTableColumn(table, SWT.NONE);
column1.setText(Resources.getMessage("ViewAttributeList.0")); //$NON-NLS-1$
column1.setWidth("32%", "30px"); //$NON-NLS-1$ //$NON-NLS-2$
DynamicTableColumn column2 = new DynamicTableColumn(table, SWT.NONE);
column2.setText(Resources.getMessage("ViewAttributeList.1")); //$NON-NLS-1$
column2.setWidth("32%", "30px"); //$NON-NLS-1$ //$NON-NLS-2$
DynamicTableColumn column3 = new DynamicTableColumn(table, SWT.NONE);
column3.setText(Resources.getMessage("ViewAttributeList.2")); //$NON-NLS-1$
column3.setWidth("32%", "30px"); //$NON-NLS-1$ //$NON-NLS-2$
column1.pack();
column2.pack();
column3.pack();
this.table.addSelectionListener(new SelectionAdapter(){
@Override public void widgetSelected(SelectionEvent arg0) {
if (model == null || model.getInputConfig() == null || model.getInputConfig().getInput() == null) {
return;
}
int index = table.getSelectionIndex();
if (index >= 0 && index <= model.getInputConfig().getInput().getHandle().getNumColumns()) {
String attribute = model.getInputConfig().getInput().getHandle().getAttributeName(index);
model.setSelectedAttribute(attribute);
controller.update(new ModelEvent(this, ModelPart.SELECTED_ATTRIBUTE, attribute));
}
}
});
// Create menu
final Menu menu = new Menu(this.table);
for (final String type : getDataTypes()) {
MenuItem item = new MenuItem(menu, SWT.NONE);
item.setText(type);
item.addSelectionListener(new SelectionAdapter(){
@Override public void widgetSelected(SelectionEvent arg0) {
actionDataTypeChanged(type);
}
});
}
// Trigger menu
this.table.addMouseListener(new MouseAdapter(){
@Override public void mouseDown(MouseEvent e) {
if (e.button == 3) {
menu.setLocation(table.toDisplay(e.x, e.y));
menu.setVisible(true);
}
}
});
}
/**
* Returns the data type of the attribute
* @param attribute
* @return
*/
private String getDataType(String attribute) {
return dataTypes[getIndexOfDataType(model.getInputDefinition().getDataType(attribute))];
}
/**
* Returns a description for the given label.
*
* @param label
* @return
*/
private DataTypeDescription<?> getDataTypeDescription(String label) {
for (DataTypeDescription<?> desc : DataType.list()) {
if (label.equals(desc.getLabel())) {
return desc;
}
}
throw new RuntimeException(Resources.getMessage("ViewAttributeDefinition.5") + label); //$NON-NLS-1$
}
/**
* Returns the format of the attribute
* @param attribute
* @return
*/
private String getDataTypeFormat(String attribute) {
DataType<?> dtype = model.getInputDefinition().getDataType(attribute);
if (!(dtype instanceof ARXOrderedString) && dtype.getDescription().hasFormat()) {
DataTypeWithFormat dtwf = (DataTypeWithFormat) dtype;
String format = dtwf.getFormat();
if (format == null) {
return Resources.getMessage("ViewAttributeDefinition.7"); //$NON-NLS-1$
} else {
return format;
}
} else {
return Resources.getMessage("ViewAttributeDefinition.8"); //$NON-NLS-1$
}
}
/**
* Returns the labels of all available data types.
*
* @return
*/
private String[] getDataTypes() {
List<String> list = new ArrayList<String>();
for (DataTypeDescription<?> desc : DataType.list()) {
list.add(desc.getLabel());
}
return list.toArray(new String[list.size()]);
}
/**
* Returns the index of a given data type.
*
* @param type
* @return
*/
private int getIndexOfDataType(DataType<?> type) {
int idx = 0;
for (DataTypeDescription<?> desc : DataType.list()) {
if (desc.getLabel().equals(type.getDescription().getLabel())) {
return idx;
}
idx++;
}
throw new RuntimeException(Resources.getMessage("ViewAttributeDefinition.6") + type.getDescription().getLabel()); //$NON-NLS-1$
}
/**
* Create an array of the values in the column for this attribute.
*
* @return
*/
private String[] getValuesAsArray(String attribute) {
final DataHandle h = model.getInputConfig().getInput().getHandle();
return h.getStatistics().getDistinctValues(h.getColumnIndexOf(attribute));
}
/**
* Create a collection of the values in the column for this attribute.
*
* @return
*/
private Collection<String> getValuesAsList(String attribute) {
return Arrays.asList(getValuesAsArray(attribute));
}
/**
* Checks whether the data type is valid.
*
* @param type
* @param values
* @return
*/
private boolean isValidDataType(DataType<?> type, Collection<String> values) {
for (String value : values) {
if (!type.isValid(value)) {
return false;
}
}
return true;
}
/**
* Update
*/
private void updateAttributeTypes() {
if (model != null && model.getInputConfig() != null && model.getInputConfig().getInput() != null) {
table.setRedraw(false);
DataHandle data = model.getInputConfig().getInput().getHandle();
for (int i = 0; i < data.getNumColumns(); i++) {
String attribute = data.getAttributeName(i);
AttributeType type = model.getInputDefinition().getAttributeType(attribute);
table.getItem(i).setImage(0, controller.getResources().getImage(type));
}
table.setRedraw(true);
SWTUtil.enable(table);
}
}
/**
* Update
*/
private void updateDataTypes() {
if (model != null && model.getInputConfig() != null && model.getInputConfig().getInput() != null) {
table.setRedraw(false);
DataHandle data = model.getInputConfig().getInput().getHandle();
for (int i = 0; i < data.getNumColumns(); i++) {
String attribute = data.getAttributeName(i);
table.getItem(i).setText(2, getDataType(attribute));
table.getItem(i).setText(3, getDataTypeFormat(attribute));
}
table.setRedraw(true);
SWTUtil.enable(table);
}
}
/**
* Updates the view.
*
* @param node
*/
private void updateEntries() {
// Check
if (model == null || model.getInputConfig() == null || model.getInputConfig().getInput() == null) {
return;
}
table.setRedraw(false);
table.removeAll();
DataHandle data = model.getInputConfig().getInput().getHandle();
for (int i = 0; i < data.getNumColumns(); i++) {
String attribute = data.getAttributeName(i);
TableItem item = new TableItem(table, SWT.NONE);
item.setText(new String[] { "", attribute, getDataType(attribute), getDataTypeFormat(attribute) }); //$NON-NLS-1$
AttributeType type = model.getInputDefinition().getAttributeType(attribute);
item.setImage(0, controller.getResources().getImage(type));
if (model.getSelectedAttribute() != null && model.getSelectedAttribute().equals(attribute)) {
table.select(i);
}
}
table.setRedraw(true);
SWTUtil.enable(table);
}
/**
* Update
* @param attribute
*/
private void updateSelectedAttribute(String attribute) {
if (model != null && model.getInputConfig() != null && model.getInputConfig().getInput() != null) {
DataHandle data = model.getInputConfig().getInput().getHandle();
for (int i = 0; i < data.getNumColumns(); i++) {
if (data.getAttributeName(i).equals(attribute)) {
table.select(i);
break;
}
}
}
}
}