/*
* 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.risk;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.deidentifier.arx.ARXPopulationModel;
import org.deidentifier.arx.gui.Controller;
import org.deidentifier.arx.gui.model.Model;
import org.deidentifier.arx.gui.model.Model.Perspective;
import org.deidentifier.arx.gui.model.ModelEvent;
import org.deidentifier.arx.gui.model.ModelEvent.ModelPart;
import org.deidentifier.arx.gui.model.ModelRisk.ViewRiskType;
import org.deidentifier.arx.gui.view.def.IView;
import org.deidentifier.arx.gui.view.impl.common.ComponentStatus;
import org.deidentifier.arx.gui.view.impl.common.ComponentStatusLabelProgressProvider;
import org.deidentifier.arx.gui.view.impl.common.async.AnalysisContext;
import org.deidentifier.arx.gui.view.impl.common.async.AnalysisContextVisualization;
import org.deidentifier.arx.risk.RiskEstimateBuilderInterruptible;
import org.deidentifier.arx.risk.RiskModelHistogram;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
/**
* This is a base class for displaying risk estimates.
*
* @author Fabian Prasser
* @param <T>
*/
public abstract class ViewRisks<T extends AnalysisContextVisualization> implements IView {
/** Our users are patient. */
public static final int MINIMAL_WORKING_TIME = 500;
/** Internal stuff. */
private AnalysisContext context = new AnalysisContext();
/** Internal stuff. */
protected final Controller controller;
/** Internal stuff. */
private Model model;
/** Internal stuff. */
private final ModelPart reset;
/** Internal stuff. */
private final ModelPart target;
/** Internal stuff. */
private final ComponentStatus status;
/** Internal stuff. */
private boolean enabled = true;
/** Internal stuff. */
private T viewContext;
/**
* Creates a new instance.
*
* @param parent
* @param controller
* @param target
* @param reset
*/
public ViewRisks( final Composite parent,
final Controller controller,
final ModelPart target,
final ModelPart reset) {
// Register
controller.addListener(ModelPart.MODEL, this);
controller.addListener(ModelPart.SELECTED_PERSPECTIVE, this);
controller.addListener(ModelPart.SELECTED_VIEW_CONFIG, this);
controller.addListener(ModelPart.SELECTED_RISK_VISUALIZATION, this);
controller.addListener(target, this);
if (reset != null) {
controller.addListener(reset, this);
}
// Remember
this.controller = controller;
this.reset = reset;
this.target = target;
// Create controls
parent.setLayout(new StackLayout());
Control control = this.createControl(parent);
// Update status
this.status = new ComponentStatus(controller,
parent,
control,
getProgressProvider());
// Reset
this.reset();
}
@Override
public void dispose() {
controller.removeListener(this);
}
/**
* Is this view enabled
* @return
*/
public boolean isEnabled() {
return enabled;
}
@Override
public void reset() {
this.doReset();
status.setEmpty();
}
/**
* Enables or disables this view
* @param enabled
*/
public void setEnabled(boolean enabled) {
if (enabled != this.enabled) {
this.enabled = enabled;
this.viewContext = null;
this.update();
}
}
@Override
public void update(final ModelEvent event) {
// Store
if (event.part == ModelPart.MODEL) {
this.model = (Model)event.data;
this.context.setModel(model);
this.context.setTarget(target);
this.viewContext = null;
this.reset();
return;
}
// Invalidate
if (event.part == ModelPart.SELECTED_VIEW_CONFIG) {
triggerUpdate();
return;
}
// Reset on null-target
if (event.part == target && event.data == null || event.part == reset) {
this.viewContext = null;
this.reset();
return;
}
// Update
if (event.part == target ||
event.part == ModelPart.SELECTED_RISK_VISUALIZATION ||
(event.part == ModelPart.SELECTED_PERSPECTIVE && model != null && model.getPerspective() == Perspective.RISK)) {
this.update();
return;
}
}
/**
* Redraws the plot.
*/
private void update() {
// Disable the view
if (!this.isEnabled()) {
this.doReset();
this.setStatusEmpty();
return;
}
// Check visibility
if (!this.status.isVisible()){
return;
}
// Check if already done
if (this.viewContext != null) {
if (!isRunning()) {
this.status.setDone();
}
return;
}
// Update
T context = createViewConfig(this.context);
if (context.isValid()) {
// Update context
this.viewContext = context;
// Update
this.doUpdate(context);
// Update status
status.setWorking();
}
}
/**
*
* Implement this to create the widget.
*
* @param parent
* @return
*/
protected abstract Control createControl(Composite parent);
/**
* Creates a view config
*
* @param context
* @return
*/
protected abstract T createViewConfig(AnalysisContext context);
/**
* Implement this to reset.
*/
protected abstract void doReset();
/**
* Implement this to update.
*
* @param context
*/
protected abstract void doUpdate(T context);
/**
* Creates a risk estimate builder
* @param context
* @return
*/
protected RiskEstimateBuilderInterruptible getBuilder(AnalysisContextRisk context) {
AnalysisContext analysisContext = context.context;
if (analysisContext.getData() == null || analysisContext.getData().definition == null) {
return null;
}
return context.handle.getRiskEstimator(analysisContext.getPopulationModel(),
analysisContext.getData().definition.getQuasiIdentifyingAttributes(),
analysisContext.getModel().getRiskModel().getSolverConfiguration())
.getInterruptibleInstance();
}
/**
* Creates a risk estimate builder
* @param context
* @param population
* @param classes
* @return
*/
protected RiskEstimateBuilderInterruptible getBuilder(AnalysisContextRisk context,
ARXPopulationModel population,
RiskModelHistogram classes) {
AnalysisContext analysisContext = context.context;
return context.handle.getRiskEstimator(population,
classes,
analysisContext.getModel().getRiskModel().getSolverConfiguration())
.getInterruptibleInstance();
}
/**
* Creates a risk estimate builder
* @param context
* @param identifiers
* @return
*/
protected RiskEstimateBuilderInterruptible getBuilder(AnalysisContextRisk context,
Set<String> identifiers) {
AnalysisContext analysisContext = context.context;
return context.handle.getRiskEstimator(analysisContext.getPopulationModel(),
identifiers,
analysisContext.getModel().getRiskModel().getSolverConfiguration())
.getInterruptibleInstance();
}
/**
* Returns the model
* @return
*/
protected Model getModel() {
return this.model;
}
/**
* May return a progress provider, if any
* @return
*/
protected abstract ComponentStatusLabelProgressProvider getProgressProvider();
/**
* Returns a string containing all quasi-identifiers
* @param context
* @return
*/
protected String getQuasiIdentifiers(AnalysisContextRisk context) {
AnalysisContext analysisContext = context.context;
List<String> list = new ArrayList<String>();
list.addAll(analysisContext.getData().definition.getQuasiIdentifyingAttributes());
Collections.sort(list);
StringBuilder builder = new StringBuilder();
for (int i=0; i<list.size(); i++) {
builder.append(list.get(i));
if (i < list.size() - 1){
builder.append(", "); //$NON-NLS-1$
}
}
return builder.toString();
}
/**
* Returns the according type of view
* @return
*/
protected abstract ViewRiskType getViewType();
/**
* Is this an input data oriented control
* @return
*/
protected boolean isInput() {
return target == ModelPart.INPUT;
}
/**
* Is a job running
* @return
*/
protected abstract boolean isRunning();
/**
* Is there still some data to show
* @return
*/
protected boolean isValid() {
if (this.target == ModelPart.INPUT) {
return this.model != null && this.model.getInputConfig() != null && this.model.getInputConfig().getInput() != null;
} else {
return this.model != null && this.model.getOutput() != null;
}
}
/**
* Status update.
*/
protected void setStatusDone(){
this.status.setDone();
}
/**
* Status empty.
*/
protected void setStatusEmpty(){
this.status.setEmpty();
}
/**
* Status working.
*/
protected void setStatusWorking(){
this.status.setWorking();
}
/**
* Triggers an update
*/
protected void triggerUpdate() {
this.viewContext = null;
this.update();
}
}