/*
* 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.utility;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.deidentifier.arx.aggregates.StatisticsBuilderInterruptible;
import org.deidentifier.arx.aggregates.StatisticsContingencyTable;
import org.deidentifier.arx.aggregates.StatisticsContingencyTable.Entry;
import org.deidentifier.arx.gui.Controller;
import org.deidentifier.arx.gui.model.ModelEvent.ModelPart;
import org.deidentifier.arx.gui.view.SWTUtil;
import org.deidentifier.arx.gui.view.impl.common.ComponentTable;
import org.deidentifier.arx.gui.view.impl.common.async.Analysis;
import org.deidentifier.arx.gui.view.impl.common.async.AnalysisContext;
import org.deidentifier.arx.gui.view.impl.common.async.AnalysisManager;
import org.deidentifier.arx.gui.view.impl.common.table.CTConfiguration;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import cern.colt.GenericSorting;
import cern.colt.Sorting;
import cern.colt.Swapper;
import cern.colt.function.IntComparator;
/**
* This class displays a contingency table.
*
* @author Fabian Prasser
*/
public class ViewStatisticsContingencyTable extends ViewStatistics<AnalysisContextContingency> {
/** Internal stuff. */
private ComponentTable table;
/** Internal stuff. */
private AnalysisManager manager;
/**
* Creates a new density plot.
*
* @param parent
* @param controller
* @param target
* @param reset
*/
public ViewStatisticsContingencyTable(final Composite parent,
final Controller controller,
final ModelPart target,
final ModelPart reset) {
super(parent, controller, target, reset, true);
this.manager = new AnalysisManager(parent.getDisplay());
}
@Override
public LayoutUtility.ViewUtilityType getType() {
return LayoutUtility.ViewUtilityType.CONTINGENCY_TABLE;
}
@Override
protected Control createControl(Composite parent) {
// Configure table
CTConfiguration config = new CTConfiguration(parent, CTConfiguration.STYLE_GRID);
config.setHorizontalAlignment(SWT.CENTER);
config.setCellSelectionEnabled(false);
config.setColumnSelectionEnabled(false);
config.setRowSelectionEnabled(false);
config.setColumnHeaderLayout(CTConfiguration.COLUMN_HEADER_LAYOUT_FILL_EQUAL);
config.setRowHeaderLayout(CTConfiguration.ROW_HEADER_LAYOUT_DEFAULT);
this.table = new ComponentTable(parent, SWT.NONE, config);
return this.table.getControl();
}
@Override
protected AnalysisContextContingency createViewConfig(AnalysisContext context) {
return new AnalysisContextContingency(context);
}
@Override
protected void doReset() {
if (this.manager != null) {
this.manager.stop();
}
this.table.clear();
setStatusEmpty();
}
@Override
protected void doUpdate(AnalysisContextContingency context) {
final int column1 = context.handle.getColumnIndexOf(context.attribute1);
final int column2 = context.handle.getColumnIndexOf(context.attribute2);
final StatisticsBuilderInterruptible builder = context.handle.getStatistics().getInterruptibleInstance();
// Create an analysis
Analysis analysis = new Analysis(){
private boolean stopped = false;
private StatisticsContingencyTable contingency;
private int[][] outputValues;
private double[][] outputFrequencies;
@Override
public int getProgress() {
return 0;
}
@Override
public void onError() {
setStatusEmpty();
}
@Override
public void onFinish() {
// Check
if (stopped || !isEnabled()) {
return;
}
// Set data
table.setData(new IDataProvider(){
@Override
public int getColumnCount() {
return contingency.values1.length;
}
@Override
public Object getDataValue(int arg0, int arg1) {
int index = Sorting.binarySearchFromTo(outputValues[arg0], arg1, 0, outputValues[arg0].length - 1);
return SWTUtil.getPrettyString((index >= 0 ? outputFrequencies[arg0][index] : 0)*100d)+"%"; //$NON-NLS-1$
}
@Override
public int getRowCount() {
return contingency.values2.length;
}
@Override
public void setDataValue(int arg0, int arg1, Object arg2) {
// Ignore
}
}, contingency.values2, contingency.values1);
setStatusDone();
}
@Override
public void onInterrupt() {
if (!isEnabled()) {
setStatusEmpty();
} else {
setStatusWorking();
}
}
@Override
public void run() throws InterruptedException {
// Timestamp
long time = System.currentTimeMillis();
// Perform work
contingency = builder.getContingencyTable(column1, column2);
@SuppressWarnings("unchecked")
List<Integer>[] inputValues = new List[contingency.values1.length];
@SuppressWarnings("unchecked")
List<Double>[] inputFrequencies = new List[contingency.values1.length];
for (int i=0; i<inputValues.length; i++){
inputValues[i] = new ArrayList<Integer>();
inputFrequencies[i] = new ArrayList<Double>();
if (stopped) return;
}
// Fill
Iterator<Entry> iter = contingency.iterator;
while (iter.hasNext()) {
if (stopped) throw new InterruptedException();
Entry p = iter.next();
inputValues[p.value1].add(p.value2);
inputFrequencies[p.value1].add(p.frequency);
}
// Convert
outputValues = new int[inputValues.length][];
outputFrequencies = new double[inputFrequencies.length][];
for (int i=0; i<outputValues.length; i++){
if (stopped) throw new InterruptedException();
List<Integer> rowValuesAsList = inputValues[i];
List<Double> rowFrequenciesAsList = inputFrequencies[i];
int[] rowValues = new int[rowValuesAsList.size()];
double[] rowFrequencies = new double[rowFrequenciesAsList.size()];
for (int j=0; j<rowValues.length; j++){
if (stopped) throw new InterruptedException();
rowValues[j] = inputValues[i].get(j);
rowFrequencies[j] = inputFrequencies[i].get(j);
}
outputValues[i] = rowValues;
outputFrequencies[i] = rowFrequencies;
}
// Sort
for (int i=0; i<outputValues.length; i++) {
if (stopped) throw new InterruptedException();
final int[] rowValues = outputValues[i];
final double[] rowFrequencies = outputFrequencies[i];
try {
GenericSorting.quickSort(0, rowValues.length, new IntComparator(){
public int compare(int arg0, int arg1) {
if (stopped) throw new RuntimeException(new InterruptedException());
return rowValues[arg0] - rowValues[arg1];
}
}, new Swapper(){
public void swap(int arg0, int arg1) {
int temp = rowValues[arg0];
rowValues[arg0] = rowValues[arg1];
rowValues[arg1] = temp;
double temp2 = rowFrequencies[arg0];
rowFrequencies[arg0] = rowFrequencies[arg1];
rowFrequencies[arg1] = temp2;
}
});
} catch (RuntimeException e) {
if (e.getCause() instanceof InterruptedException){
throw (InterruptedException)e.getCause();
} else {
throw e;
}
}
}
// Our users are patient
while (System.currentTimeMillis() - time < MINIMAL_WORKING_TIME && !stopped){
Thread.sleep(10);
}
}
@Override
public void stop() {
stopped = true;
builder.interrupt();
}
};
this.manager.start(analysis);
}
/**
* Is an analysis running
*/
protected boolean isRunning() {
return manager != null && manager.isRunning();
}
}