/* * ARX: Powerful Data Anonymization * Copyright 2012 - 2017 Fabian Prasser, Florian Kohlmayer and contributors * Copyright 2014 Karol Babioch <karol@babioch.de> * * 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; import java.awt.Desktop; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.Set; import org.apache.commons.math3.util.Pair; import org.deidentifier.arx.ARXLattice.ARXNode; import org.deidentifier.arx.ARXLattice.Anonymity; import org.deidentifier.arx.ARXResult; import org.deidentifier.arx.AttributeType; import org.deidentifier.arx.AttributeType.Hierarchy; import org.deidentifier.arx.Data; import org.deidentifier.arx.DataDefinition; import org.deidentifier.arx.DataHandle; import org.deidentifier.arx.DataScale; import org.deidentifier.arx.DataSelector; import org.deidentifier.arx.DataSubset; import org.deidentifier.arx.DataType; import org.deidentifier.arx.DataType.DataTypeDescription; import org.deidentifier.arx.RowSet; import org.deidentifier.arx.aggregates.HierarchyBuilder; import org.deidentifier.arx.exceptions.RollbackRequiredException; import org.deidentifier.arx.gui.model.Model; import org.deidentifier.arx.gui.model.ModelAuditTrailEntry; import org.deidentifier.arx.gui.model.ModelCriterion; import org.deidentifier.arx.gui.model.ModelDDisclosurePrivacyCriterion; import org.deidentifier.arx.gui.model.ModelEvent; import org.deidentifier.arx.gui.model.ModelEvent.ModelPart; import org.deidentifier.arx.gui.model.ModelBLikenessCriterion; import org.deidentifier.arx.gui.model.ModelExplicitCriterion; import org.deidentifier.arx.gui.model.ModelLDiversityCriterion; import org.deidentifier.arx.gui.model.ModelNodeFilter; import org.deidentifier.arx.gui.model.ModelRiskBasedCriterion; import org.deidentifier.arx.gui.model.ModelTClosenessCriterion; import org.deidentifier.arx.gui.model.ModelViewConfig; import org.deidentifier.arx.gui.model.ModelViewConfig.Mode; import org.deidentifier.arx.gui.resources.Resources; import org.deidentifier.arx.gui.view.def.IView; import org.deidentifier.arx.gui.view.impl.MainWindow; import org.deidentifier.arx.gui.view.impl.menu.DialogProject; import org.deidentifier.arx.gui.view.impl.menu.DialogProperties; import org.deidentifier.arx.gui.view.impl.menu.DialogQueryResult; import org.deidentifier.arx.gui.view.impl.menu.DialogSeparator; import org.deidentifier.arx.gui.view.impl.wizard.ARXWizard; import org.deidentifier.arx.gui.view.impl.wizard.HierarchyWizard; import org.deidentifier.arx.gui.view.impl.wizard.HierarchyWizard.HierarchyWizardResult; import org.deidentifier.arx.gui.view.impl.wizard.ImportWizard; import org.deidentifier.arx.gui.worker.Worker; import org.deidentifier.arx.gui.worker.WorkerAnonymize; import org.deidentifier.arx.gui.worker.WorkerCreateCertificate; import org.deidentifier.arx.gui.worker.WorkerExport; import org.deidentifier.arx.gui.worker.WorkerImport; import org.deidentifier.arx.gui.worker.WorkerLoad; import org.deidentifier.arx.gui.worker.WorkerLocalRecode; import org.deidentifier.arx.gui.worker.WorkerSave; import org.deidentifier.arx.gui.worker.WorkerTransform; import org.deidentifier.arx.io.CSVDataOutput; import org.deidentifier.arx.io.ImportConfiguration; import org.deidentifier.arx.io.ImportConfigurationCSV; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import cern.colt.Swapper; import de.linearbits.swt.choicesdialog.ChoiceItem; import de.linearbits.swt.choicesdialog.ChoicesDialog; /** * The main controller for the whole tool. * * @author Fabian Prasser */ public class Controller implements IView { /** The debug data. */ private final DebugData debug = new DebugData(); /** Listeners registered by the views. */ private final Map<ModelPart, Set<IView>> listeners = Collections.synchronizedMap(new HashMap<ModelPart, Set<IView>>()); /** The main window. */ private final MainWindow main; /** The model. */ private Model model; /** The resources. */ private final Resources resources; /** * Creates a new controller. * * @param main */ public Controller(final MainWindow main) { this.main = main; this.resources = new Resources(main.getShell()); } /** * Applies local recoding */ public void actionApplyLocalRecoding() { // Run the worker final WorkerLocalRecode worker = new WorkerLocalRecode(model); main.showProgressDialog(Resources.getMessage("Controller.140"), worker); //$NON-NLS-1$ // Show errors if (worker.getError() != null) { // Extract Throwable t = worker.getError(); if (worker.getError() instanceof InvocationTargetException) { t = worker.getError().getCause(); } if (t instanceof RollbackRequiredException) { // Rollback this.actionApplySelectedTransformation(); return; } else if (t instanceof OutOfMemoryError) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.13"), //$NON-NLS-1$ Resources.getMessage("Controller.120")); //$NON-NLS-1$ } else if (t instanceof NullPointerException) { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.36"), t); //$NON-NLS-1$ } else { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.13"), //$NON-NLS-1$ Resources.getMessage("Controller.141") + t.getMessage()); //$NON-NLS-1$ } } update(new ModelEvent(this, ModelPart.OUTPUT, model.getOutput())); // Do not sort if dataset is too large if (model.getMaximalSizeForComplexOperations() == 0 || model.getInputConfig().getInput().getHandle().getNumRows() <= model.getMaximalSizeForComplexOperations()) { this.model.getViewConfig().setMode(Mode.GROUPED); this.updateViewConfig(true); } else { this.model.getViewConfig().setMode(Mode.UNSORTED); this.updateViewConfig(true); } this.update(new ModelEvent(this, ModelPart.SELECTED_VIEW_CONFIG, model.getOutput())); } /** * Applies the selected transformation. */ public void actionApplySelectedTransformation() { // Run the worker final WorkerTransform worker = new WorkerTransform(model); main.showProgressDialog(Resources.getMessage("Controller.0"), worker); //$NON-NLS-1$ // Show errors if (worker.getError() != null) { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.2"), //$NON-NLS-1$ worker.getError()); return; } // Distribute results if (worker.getResult() != null) { this.model.setOutput(worker.getResult(), model.getSelectedNode()); this.update(new ModelEvent(this, ModelPart.OUTPUT, worker.getResult())); // Do not sort if dataset is too large if (model.getMaximalSizeForComplexOperations() == 0 || model.getInputConfig().getInput().getHandle().getNumRows() <= model.getMaximalSizeForComplexOperations()) { this.model.getViewConfig().setMode(Mode.GROUPED); this.updateViewConfig(true); } else { this.model.getViewConfig().setMode(Mode.UNSORTED); this.updateViewConfig(true); } this.update(new ModelEvent(this, ModelPart.SELECTED_VIEW_CONFIG, model.getOutput())); } } /** * Clears the event log. */ public void actionClearEventLog() { this.debug.clearEventLog(); } /** * Adds a criterion */ public void actionCriterionAdd() { List<ModelCriterion> criteria = new ArrayList<ModelCriterion>(); if (!model.getDifferentialPrivacyModel().isEnabled()) { criteria.add(model.getDifferentialPrivacyModel()); } if (!model.getKAnonymityModel().isEnabled()) { criteria.add(model.getKAnonymityModel()); } if (!model.getKMapModel().isEnabled()) { criteria.add(model.getKMapModel()); } if (!model.getDPresenceModel().isEnabled()) { criteria.add(model.getDPresenceModel()); } if (!model.getStackelbergModel().isEnabled()) { criteria.add(model.getStackelbergModel()); } Set<String> sensitive = model.getInputDefinition().getSensitiveAttributes(); List<ModelExplicitCriterion> explicit = new ArrayList<ModelExplicitCriterion>(); for (ModelLDiversityCriterion other : model.getLDiversityModel().values()) { if (!other.isEnabled() && sensitive.contains(other.getAttribute())) { explicit.add(other); } } for (ModelTClosenessCriterion other : model.getTClosenessModel().values()) { if (!other.isEnabled() && sensitive.contains(other.getAttribute())) { explicit.add(other); } } for (ModelDDisclosurePrivacyCriterion other : model.getDDisclosurePrivacyModel().values()) { if (!other.isEnabled() && sensitive.contains(other.getAttribute())) { explicit.add(other); } } for (ModelBLikenessCriterion other : model.getBLikenessModel().values()) { if (!other.isEnabled() && sensitive.contains(other.getAttribute())) { explicit.add(other); } } Collections.sort(explicit, new Comparator<ModelExplicitCriterion>(){ public int compare(ModelExplicitCriterion o1, ModelExplicitCriterion o2) { return o1.getAttribute().compareTo(o2.getAttribute()); } }); criteria.addAll(explicit); List<ModelRiskBasedCriterion> riskBased = new ArrayList<ModelRiskBasedCriterion>(); for (ModelRiskBasedCriterion other : model.getRiskBasedModel()) { if (!other.isEnabled()) { riskBased.add(other); } } Collections.sort(riskBased, new Comparator<ModelRiskBasedCriterion>(){ public int compare(ModelRiskBasedCriterion o1, ModelRiskBasedCriterion o2) { return o1.getLabel().compareTo(o2.getLabel()); } }); criteria.addAll(riskBased); if (criteria.isEmpty()) { return; } // Select criterion ModelCriterion criterion = main.showAddCriterionDialog(model, criteria); if (criterion != null) { criterion.setEnabled(true); this.update(new ModelEvent(this, ModelPart.CRITERION_DEFINITION, criterion)); } } /** * Configures a criterion * @param criterion */ public void actionCriterionConfigure(ModelCriterion criterion) { List<ModelCriterion> criteria = new ArrayList<ModelCriterion>(); if (model.getDifferentialPrivacyModel().isEnabled()) { criteria.add(model.getDifferentialPrivacyModel()); } if (model.getKAnonymityModel().isEnabled()) { criteria.add(model.getKAnonymityModel()); } if (model.getKMapModel().isEnabled()) { criteria.add(model.getKMapModel()); } if (model.getDPresenceModel().isEnabled()) { criteria.add(model.getDPresenceModel()); } if (model.getStackelbergModel().isEnabled()) { criteria.add(model.getStackelbergModel()); } Set<String> sensitive = model.getInputDefinition().getSensitiveAttributes(); List<ModelExplicitCriterion> explicit = new ArrayList<ModelExplicitCriterion>(); for (ModelLDiversityCriterion other : model.getLDiversityModel().values()) { if (other.isEnabled() && sensitive.contains(other.getAttribute())) { explicit.add(other); } } for (ModelTClosenessCriterion other : model.getTClosenessModel().values()) { if (other.isEnabled() && sensitive.contains(other.getAttribute())) { explicit.add(other); } } for (ModelDDisclosurePrivacyCriterion other : model.getDDisclosurePrivacyModel().values()) { if (other.isEnabled() && sensitive.contains(other.getAttribute())) { explicit.add(other); } } for (ModelBLikenessCriterion other : model.getBLikenessModel().values()) { if (other.isEnabled() && sensitive.contains(other.getAttribute())) { explicit.add(other); } } Collections.sort(explicit, new Comparator<ModelExplicitCriterion>(){ public int compare(ModelExplicitCriterion o1, ModelExplicitCriterion o2) { return o1.getAttribute().compareTo(o2.getAttribute()); } }); criteria.addAll(explicit); List<ModelRiskBasedCriterion> riskBased = new ArrayList<ModelRiskBasedCriterion>(); for (ModelRiskBasedCriterion other : model.getRiskBasedModel()) { if (other.isEnabled()) { riskBased.add(other); } } Collections.sort(riskBased, new Comparator<ModelRiskBasedCriterion>(){ public int compare(ModelRiskBasedCriterion o1, ModelRiskBasedCriterion o2) { return o1.getLabel().compareTo(o2.getLabel()); } }); criteria.addAll(riskBased); if (criteria.isEmpty()) { return; } // Select criterion main.showConfigureCriterionDialog(model, criteria, criterion); this.update(new ModelEvent(this, ModelPart.CRITERION_DEFINITION, null)); } /** * Enables and disables a criterion. * * @param criterion */ public void actionCriterionEnable(ModelCriterion criterion) { if (criterion.isEnabled()) { criterion.setEnabled(false); } else { criterion.setEnabled(true); } update(new ModelEvent(this, ModelPart.CRITERION_DEFINITION, criterion)); } /** * Pull settings into the criterion. * * @param criterion */ public void actionCriterionPull(ModelCriterion criterion) { // Collect all other criteria List<ModelExplicitCriterion> others = new ArrayList<ModelExplicitCriterion>(); if (criterion instanceof ModelLDiversityCriterion) { for (ModelLDiversityCriterion other : model.getLDiversityModel().values()) { if (!other.equals(criterion) && other.isEnabled()) { others.add((ModelExplicitCriterion) other); } } } else if (criterion instanceof ModelTClosenessCriterion) { for (ModelTClosenessCriterion other : model.getTClosenessModel().values()) { if (!other.equals(criterion) && other.isEnabled()) { others.add((ModelExplicitCriterion) other); } } } else if (criterion instanceof ModelDDisclosurePrivacyCriterion) { for (ModelDDisclosurePrivacyCriterion other : model.getDDisclosurePrivacyModel().values()) { if (!other.equals(criterion) && other.isEnabled()) { others.add((ModelExplicitCriterion) other); } } } else if (criterion instanceof ModelBLikenessCriterion) { for (ModelBLikenessCriterion other : model.getBLikenessModel().values()) { if (!other.equals(criterion) && other.isEnabled()) { others.add((ModelExplicitCriterion) other); } } } else { throw new RuntimeException(Resources.getMessage("Controller.1")); //$NON-NLS-1$ } // Break if none found if (others.isEmpty()) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.95"), //$NON-NLS-1$ Resources.getMessage("Controller.96")); //$NON-NLS-1$ return; } // Select criterion ModelExplicitCriterion element = main.showSelectCriterionDialog(others); if (element == null) { return; } else { ((ModelExplicitCriterion) criterion).pull(element); update(new ModelEvent(this, ModelPart.CRITERION_DEFINITION, criterion)); } } /** * Pushes the settings of the criterion. * * @param criterion */ public void actionCriterionPush(ModelCriterion criterion) { if (main.showQuestionDialog(main.getShell(), Resources.getMessage("Controller.93"), //$NON-NLS-1$ Resources.getMessage("Controller.94"))) { //$NON-NLS-1$ if (criterion instanceof ModelLDiversityCriterion) { for (ModelLDiversityCriterion other : model.getLDiversityModel().values()) { if (!other.equals(criterion) && other.isEnabled()) { other.pull((ModelExplicitCriterion) criterion); } } } else if (criterion instanceof ModelTClosenessCriterion) { for (ModelTClosenessCriterion other : model.getTClosenessModel().values()) { if (!other.equals(criterion) && other.isEnabled()) { other.pull((ModelExplicitCriterion) criterion); } } } else if (criterion instanceof ModelDDisclosurePrivacyCriterion) { for (ModelDDisclosurePrivacyCriterion other : model.getDDisclosurePrivacyModel().values()) { if (!other.equals(criterion) && other.isEnabled()) { other.pull((ModelExplicitCriterion) criterion); } } } else if (criterion instanceof ModelBLikenessCriterion) { for (ModelBLikenessCriterion other : model.getBLikenessModel().values()) { if (!other.equals(criterion) && other.isEnabled()) { other.pull((ModelExplicitCriterion) criterion); } } } else { throw new RuntimeException(Resources.getMessage("Controller.15")); //$NON-NLS-1$ } update(new ModelEvent(this, ModelPart.CRITERION_DEFINITION, criterion)); } } /** * Toggles the "show groups" option. */ public void actionDataShowGroups() { // Break if no output if (model.getOutput() == null) return; this.model.getViewConfig().setMode(Mode.GROUPED); this.updateViewConfig(false); // Update this.update(new ModelEvent(this, ModelPart.SELECTED_VIEW_CONFIG, model.getOutput())); } /** * Sorts the data. * * @param input */ public void actionDataSort(boolean input) { // Break if no attribute selected if (model.getSelectedAttribute() == null) return; if (input) { this.model.getViewConfig().setMode(Mode.SORTED_INPUT); } else { this.model.getViewConfig().setMode(Mode.SORTED_OUTPUT); } this.model.getViewConfig().setAttribute(model.getSelectedAttribute()); this.updateViewConfig(false); // Update this.update(new ModelEvent(this, ModelPart.SELECTED_VIEW_CONFIG, model.getOutput())); } /** * Toggles the "subset" option. */ public void actionDataToggleSubset() { // Break if no output if (model.getOutput() == null) return; // Update boolean val = !model.getViewConfig().isSubset(); this.model.getViewConfig().setSubset(val); this.updateViewConfig(false); this.update(new ModelEvent(this, ModelPart.SELECTED_VIEW_CONFIG, model.getOutput())); } /** * Expand action * @param transformation */ public void actionExpand(ARXNode transformation) { model.getResult().getLattice().expand(transformation); } /** * Starts the anonymization. * @param heuristicSearch */ public void actionMenuEditAnonymize(boolean heuristicSearch) { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.3"), //$NON-NLS-1$ Resources.getMessage("Controller.4")); //$NON-NLS-1$ return; } if (model.getInputConfig().getInput() == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.5"), //$NON-NLS-1$ Resources.getMessage("Controller.6")); //$NON-NLS-1$ return; } if (model.getInputConfig().getResearchSubset().size() == 0) { final String message = Resources.getMessage("Controller.100"); //$NON-NLS-1$ main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.11"), message); //$NON-NLS-1$ return; } // Query for execution time int timeLimit = 0; if (heuristicSearch) { String output = this.actionShowInputDialog(main.getShell(), Resources.getMessage("Controller.38"), //$NON-NLS-1$ Resources.getMessage("Controller.79") + //$NON-NLS-1$ Resources.getMessage("Controller.80"), "0.5", //$NON-NLS-1$ //$NON-NLS-2$ new IInputValidator(){ public String isValid(String arg0) { // TODO: Ugly hack try { double val = Double.parseDouble(arg0); return val>0d ? null : Resources.getMessage("Controller.98"); //$NON-NLS-1$ } catch (Exception e) { return Resources.getMessage("Controller.99"); //$NON-NLS-1$ } } }); if (output == null) { return; } timeLimit = Double.valueOf(Double.valueOf(output) * 1000d).intValue(); } // Reset actionMenuEditReset(); // Run the worker final WorkerAnonymize worker = new WorkerAnonymize(model, timeLimit); main.showProgressDialog(Resources.getMessage("Controller.12"), worker); //$NON-NLS-1$ // Show errors if (worker.getError() != null) { // Extract Throwable t = worker.getError(); if (worker.getError() instanceof InvocationTargetException) { t = worker.getError().getCause(); } if (t instanceof OutOfMemoryError) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.13"), //$NON-NLS-1$ Resources.getMessage("Controller.120")); //$NON-NLS-1$ } else if (t instanceof NullPointerException) { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.36"), t); //$NON-NLS-1$ } else { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.13"), //$NON-NLS-1$ Resources.getMessage("Controller.14") + t.getMessage()); //$NON-NLS-1$ } // Log StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); worker.getError().printStackTrace(pw); getResources().getLogger().info(sw.toString()); // Reset model.setOutput(null, null); model.setSelectedNode(null); update(new ModelEvent(this, ModelPart.OUTPUT, null)); update(new ModelEvent(this, ModelPart.SELECTED_NODE, null)); return; } // Distribute results if (worker.getResult() != null) { // Retrieve optimal result final ARXResult result = worker.getResult(); model.createClonedConfig(); model.setResult(result); model.getClipboard().clearClipboard(); // Create filter ModelNodeFilter filter = new ModelNodeFilter(result.getLattice().getTop().getTransformation(), model.getInitialNodesInViewer()); filter.initialize(result); model.setNodeFilter(filter); // Update model update(new ModelEvent(this, ModelPart.FILTER, filter)); update(new ModelEvent(this, ModelPart.RESULT, result)); model.getClipboard().addInterestingTransformations(model); update(new ModelEvent(this, ModelPart.CLIPBOARD, null)); if (result.isResultAvailable()) { model.setOutput(result.getOutput(false), result.getGlobalOptimum()); model.setSelectedNode(result.getGlobalOptimum()); update(new ModelEvent(this, ModelPart.OUTPUT, result.getOutput(false))); update(new ModelEvent(this, ModelPart.SELECTED_NODE, result.getGlobalOptimum())); // Do not sort if dataset is too large if (model.getMaximalSizeForComplexOperations() == 0 || model.getInputConfig().getInput().getHandle().getNumRows() <= model.getMaximalSizeForComplexOperations()) { this.model.getViewConfig().setSubset(true); this.model.getViewConfig().setMode(Mode.GROUPED); this.updateViewConfig(true); } else { this.model.getViewConfig().setSubset(true); this.model.getViewConfig().setMode(Mode.UNSORTED); } this.update(new ModelEvent(this, ModelPart.SELECTED_VIEW_CONFIG, model.getOutput())); } else { model.setOutput(null, null); model.setSelectedNode(null); update(new ModelEvent(this, ModelPart.OUTPUT, null)); update(new ModelEvent(this, ModelPart.SELECTED_NODE, null)); } // Update selected attribute update(new ModelEvent(this, ModelPart.SELECTED_ATTRIBUTE, model.getSelectedAttribute())); } } /** * Starts the wizard. */ public void actionMenuEditCreateHierarchy() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.16"), //$NON-NLS-1$ Resources.getMessage("Controller.17")); //$NON-NLS-1$ return; } else if (model.getInputConfig().getInput() == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.18"), //$NON-NLS-1$ Resources.getMessage("Controller.19")); //$NON-NLS-1$ return; } String attr = model.getSelectedAttribute(); int index = model.getInputConfig() .getInput() .getHandle() .getColumnIndexOf(attr); DataType<?> type = model.getInputDefinition().getDataType(attr); String[] data = model.getInputConfig() .getInput() .getHandle() .getStatistics() .getDistinctValues(index); HierarchyBuilder<?> builder = model.getInputConfig().getHierarchyBuilder(attr); @SuppressWarnings({ "unchecked", "rawtypes" }) ARXWizard<HierarchyWizardResult<?>> wizard = new HierarchyWizard(this, attr, builder, type, model.getLocale(), data); if (wizard.open(main.getShell())) { HierarchyWizardResult<?> result = wizard.getResult(); if (result.hierarchy != null) { model.getInputConfig().setMaximumGeneralization(attr, null); model.getInputConfig().setMinimumGeneralization(attr, null); model.getInputConfig().setHierarchy(attr, result.hierarchy); model.getInputConfig().setHierarchyBuilder(attr, result.builder); update(new ModelEvent(this, ModelPart.HIERARCHY, result.hierarchy)); } } } /** * Initializes the hierarchy for the currently selected attribute with a scheme * for top-/bottom coding. */ public void actionMenuEditCreateTopBottomCodingHierarchy() { // Check if (model == null || model.getInputConfig() == null || model.getInputConfig().getInput() == null || model.getSelectedAttribute() == null) { return; } // Check DataType<?>type = model.getInputDefinition().getDataType(model.getSelectedAttribute()); if (type.getDescription().getScale() != DataScale.INTERVAL && type.getDescription().getScale() != DataScale.RATIO) { actionShowInfoDialog(this.main.getShell(), Resources.getMessage("Controller.150"), Resources.getMessage("Controller.151")); //$NON-NLS-1$ //$NON-NLS-2$ return; } // User input Pair<Pair<String, Boolean>, Pair<String, Boolean>> pair = main.showTopBottomCodingDialog(type); if (pair == null || (pair.getFirst() == null && pair.getSecond() == null)) { return; } // Obtain values DataHandle handle = model.getInputConfig().getInput().getHandle(); int index = handle.getColumnIndexOf(model.getSelectedAttribute()); String[] values = handle.getStatistics().getDistinctValuesOrdered(index); String bottom = pair.getFirst() != null ? pair.getFirst().getFirst() : null; Boolean bottomInclusive = pair.getFirst() != null ? pair.getFirst().getSecond() : null; String top = pair.getSecond() != null ? pair.getSecond().getFirst() : null; Boolean topInclusive = pair.getSecond() != null ? pair.getSecond().getSecond() : null; // Create hierarchy String[][] array; try { array = new String[values.length][2]; for (int i = 0; i < values.length; i++) { String value = values[i]; String coded = null; if (top != null) { int cmp = type.compare(value, top); if (cmp > 0 || (topInclusive && cmp == 0)) { coded = topInclusive ? ">=" + top : ">" + top; //$NON-NLS-1$ //$NON-NLS-2$ } } if (coded == null && bottom != null) { int cmp = type.compare(value, bottom); if (cmp < 0 || (bottomInclusive && cmp == 0)) { coded = bottomInclusive ? "<=" + bottom : "<" + bottom; //$NON-NLS-1$ //$NON-NLS-2$ } } if (coded == null) { coded = value; } array[i] = new String[] {value, coded}; } } catch (Exception e) { this.actionShowInfoDialog(main.getShell(), Resources.getMessage("Controller.152"), Resources.getMessage("Controller.153")); //$NON-NLS-1$ //$NON-NLS-2$ return; } // Update Hierarchy hierarchy = Hierarchy.create(array); this.model.getInputConfig().setHierarchy(model.getSelectedAttribute(), hierarchy); this.update(new ModelEvent(this, ModelPart.HIERARCHY, hierarchy)); } /** * Find and replace action */ public void actionMenuEditFindReplace() { // Check if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.3"), //$NON-NLS-1$ Resources.getMessage("Controller.4")); //$NON-NLS-1$ return; } // Check if (model.getInputConfig().getInput() == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.5"), //$NON-NLS-1$ Resources.getMessage("Controller.6")); //$NON-NLS-1$ return; } // Show dialog DataHandle handle = model.getInputConfig().getInput().getHandle(); int column = handle.getColumnIndexOf(model.getSelectedAttribute()); Pair<String, String> pair = main.showFindReplaceDialog(model, handle, column); // If action must be performed if (pair != null) { // Replace in input handle.replace(column, pair.getFirst(), pair.getSecond()); // Replace in output if (model.getOutputConfig() != null) { Hierarchy hierarchy = model.getOutputConfig().getHierarchy(model.getSelectedAttribute()); if (hierarchy != null) { for (String[] array : hierarchy.getHierarchy()) { for (int i=0; i<array.length; i++) { if (array[i].equals(pair.getFirst())) { array[i] = pair.getSecond(); } } } } } // Replace in input hierarchy if (model.getInputConfig() != null) { Hierarchy hierarchy = model.getInputConfig().getHierarchy(model.getSelectedAttribute()); if (hierarchy != null) { for (String[] array : hierarchy.getHierarchy()) { for (int i=0; i<array.length; i++) { if (array[i].equals(pair.getFirst())) { array[i] = pair.getSecond(); } } } } } // Fire event ModelAuditTrailEntry entry = ModelAuditTrailEntry.createfindReplaceEntry( model.getSelectedAttribute(), pair.getFirst(), pair.getSecond()); update(new ModelEvent(this, ModelPart.ATTRIBUTE_VALUE, entry)); // Store in model model.addAuditTrailEntry(entry); model.setModified(); } } /** * Initializes the hierarchy for the currently selected attribute */ public void actionMenuEditInitializeHierarchy() { // Check if (model == null || model.getInputConfig() == null || model.getInputConfig().getInput() == null || model.getSelectedAttribute() == null) { return; } // Obtain values DataHandle handle = model.getInputConfig().getInput().getHandle(); int index = handle.getColumnIndexOf(model.getSelectedAttribute()); String[] values = handle.getStatistics().getDistinctValuesOrdered(index); // Create hierarchy String[][] array = new String[values.length][0]; for (int i = 0; i < values.length; i++) { array[i] = new String[] { values[i] }; } // Update Hierarchy hierarchy = Hierarchy.create(array); this.model.getInputConfig().setHierarchy(model.getSelectedAttribute(), hierarchy); this.update(new ModelEvent(this, ModelPart.HIERARCHY, hierarchy)); } /** * Resets the current output */ public void actionMenuEditReset() { // Reset output model.getViewConfig().setMode(Mode.UNSORTED); model.getViewConfig().setSubset(false); model.setGroups(null); model.setResult(null); model.setOutputConfig(null); model.setOutput(null, null); model.setSelectedNode(null); model.getClipboard().clearClipboard(); update(new ModelEvent(this, ModelPart.SELECTED_VIEW_CONFIG, null)); update(new ModelEvent(this, ModelPart.RESULT, null)); update(new ModelEvent(this, ModelPart.OUTPUT, null)); update(new ModelEvent(this, ModelPart.SELECTED_NODE, null)); } /** * Starts the "edit settings" dialog. */ public void actionMenuEditSettings() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.22"), //$NON-NLS-1$ Resources.getMessage("Controller.23")); //$NON-NLS-1$ return; } try { final DialogProperties dialog = new DialogProperties(main.getShell(), model, main.getController()); dialog.open(); // Update the title // TODO: This is not sound ((IView) main).update(new ModelEvent(this, ModelPart.MODEL, model)); } catch (final Exception e) { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.25"), e); //$NON-NLS-1$ getResources().getLogger().info(e); } } /** * Creates and displays a certificate */ public void actionMenuFileCreateCertificate() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.30"), //$NON-NLS-1$ Resources.getMessage("Controller.31")); //$NON-NLS-1$ return; } else if (model.getOutput() == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.32"), //$NON-NLS-1$ Resources.getMessage("Controller.33")); //$NON-NLS-1$ return; } // Check node if (model.getOutputNode().getAnonymity() != Anonymity.ANONYMOUS) { if (!main.showQuestionDialog(main.getShell(), Resources.getMessage("Controller.34"), //$NON-NLS-1$ Resources.getMessage("Controller.156"))) //$NON-NLS-1$ { return; } } // Ask for file String file = main.showSaveFileDialog(main.getShell(), "*.pdf"); //$NON-NLS-1$ if (file == null) { return; } if (!file.endsWith(".pdf")) { //$NON-NLS-1$ file = file + ".pdf"; //$NON-NLS-1$ } // Export final WorkerCreateCertificate worker = new WorkerCreateCertificate(file, model.getCSVSyntax(), model.getInputConfig().getInput().getHandle(), model.getOutputDefinition(), model.getOutputConfig().getConfig(), model.getResult(), model.getOutputNode(), model.getOutput(), model); main.showProgressDialog(Resources.getMessage("Controller.154"), worker); //$NON-NLS-1$ if (worker.getError() != null) { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.155"), //$NON-NLS-1$ worker.getError()); return; } // Open if (Desktop.isDesktopSupported()) { try { Desktop.getDesktop().open(worker.getResult()); } catch (Exception e) { // Drop silently } } } /** * File->exit. */ public void actionMenuFileExit() { // Prepare ChoiceItem[] items; boolean modified = model != null && model.isModified(); // Build items if (modified) { items = new ChoiceItem[3]; items[0] = new ChoiceItem(Resources.getMessage("Controller.110"), //$NON-NLS-1$ Resources.getMessage("Controller.111")); //$NON-NLS-1$ items[1] = new ChoiceItem(Resources.getMessage("Controller.112"), //$NON-NLS-1$ Resources.getMessage("Controller.113")); //$NON-NLS-1$ } else { items = new ChoiceItem[2]; items[0] = new ChoiceItem(Resources.getMessage("Controller.114"), //$NON-NLS-1$ Resources.getMessage("Controller.115")); //$NON-NLS-1$ } items[items.length-1] = new ChoiceItem(Resources.getMessage("Controller.116"), //$NON-NLS-1$ Resources.getMessage("Controller.117")); //$NON-NLS-1$ // Show dialog ChoicesDialog dialog = new ChoicesDialog(main.getShell(), SWT.APPLICATION_MODAL); dialog.setTitle(Resources.getMessage("Controller.26")); //$NON-NLS-1$ dialog.setMessage(Resources.getMessage("Controller.27"));//$NON-NLS-1$ dialog.setImage(Display.getCurrent().getSystemImage(SWT.ICON_QUESTION)); dialog.setChoices(items); dialog.setDefaultChoice(items[items.length-1]); int choice = dialog.open(); // Cancel if (choice == -1 || choice == items.length-1) { return; } // Save if (modified && choice == 0) { actionMenuFileSave(); } // Exit main.getShell().getDisplay().dispose(); System.exit(0); } /** * File->export data. */ public void actionMenuFileExportData() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.30"), //$NON-NLS-1$ Resources.getMessage("Controller.31")); //$NON-NLS-1$ return; } else if (model.getOutput() == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.32"), //$NON-NLS-1$ Resources.getMessage("Controller.33")); //$NON-NLS-1$ return; } // Check node if (model.getOutputNode().getAnonymity() != Anonymity.ANONYMOUS) { if (!main.showQuestionDialog(main.getShell(), Resources.getMessage("Controller.34"), //$NON-NLS-1$ Resources.getMessage("Controller.35"))) //$NON-NLS-1$ { return; } } // Ask for file String file = main.showSaveFileDialog(main.getShell(), "*.csv"); //$NON-NLS-1$ if (file == null) { return; } if (!file.endsWith(".csv")) { //$NON-NLS-1$ file = file + ".csv"; //$NON-NLS-1$ } // Export final WorkerExport worker = new WorkerExport(file, model.getCSVSyntax(), model.getOutput(), model.getOutputConfig().getConfig(), model.getInputBytes()); main.showProgressDialog(Resources.getMessage("Controller.39"), worker); //$NON-NLS-1$ if (worker.getError() != null) { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.41"), //$NON-NLS-1$ worker.getError()); return; } } /** * File->Export hierarchy. */ public void actionMenuFileExportHierarchy() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.42"), //$NON-NLS-1$ Resources.getMessage("Controller.43")); //$NON-NLS-1$ return; } Hierarchy hierarchy = model.getInputConfig().getHierarchy(model.getSelectedAttribute()); if (hierarchy == null || hierarchy.getHierarchy() == null || hierarchy.getHierarchy().length == 0 || hierarchy.getHierarchy()[0].length == 0) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.91"), //$NON-NLS-1$ Resources.getMessage("Controller.92")); //$NON-NLS-1$ return; } // Ask for file String file = main.showSaveFileDialog(main.getShell(), "*.csv"); //$NON-NLS-1$ if (file == null) { return; } if (!file.endsWith(".csv")) { //$NON-NLS-1$ file = file + ".csv"; //$NON-NLS-1$ } // Save try { final CSVDataOutput out = new CSVDataOutput(file, model.getCSVSyntax()); out.write(hierarchy.getHierarchy()); } catch (final Exception e) { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.50"), e); //$NON-NLS-1$ } } /** * File->Import data. */ public void actionMenuFileImportData() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.51"), //$NON-NLS-1$ Resources.getMessage("Controller.52")); //$NON-NLS-1$ return; } //Shows an 'Are you sure?' dialog if data has been already imported if (model.getInputConfig() != null && model.getInputConfig().getInput() != null && !main.showQuestionDialog(main.getShell(), Resources.getMessage("Controller.101"), //$NON-NLS-1$ Resources.getMessage("Controller.102"))) { //$NON-NLS-1$ return; } ARXWizard<ImportConfiguration> wizard = new ImportWizard(this, model); if (wizard.open(main.getShell())) { ImportConfiguration config = wizard.getResult(); if (config != null) { actionImportData(config); } } } /** * File->Import hierarchy. */ public void actionMenuFileImportHierarchy() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.54"), //$NON-NLS-1$ Resources.getMessage("Controller.55")); //$NON-NLS-1$ return; } else if (model.getInputConfig().getInput() == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.56"), //$NON-NLS-1$ Resources.getMessage("Controller.57")); //$NON-NLS-1$ return; } final String path = actionShowOpenFileDialog(main.getShell(), "*.csv"); //$NON-NLS-1$ if (path != null) { // Determine separator DialogSeparator dialog = null; try { dialog = new DialogSeparator(main.getShell(), this, path, false); dialog.create(); if (dialog.open() == Window.CANCEL) { return; } } catch (Throwable error) { if (error instanceof RuntimeException) { if (error.getCause() != null) { error = error.getCause(); } } if ((error instanceof IllegalArgumentException) || (error instanceof IOException)) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.37"), error.getMessage()); //$NON-NLS-1$ } else { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.78"), error); //$NON-NLS-1$ } return; } // Load hierarchy final char separator = dialog.getSeparator(); final Charset charset = Charset.defaultCharset(); final Hierarchy hierarchy = actionImportHierarchy(path, charset, separator); if (hierarchy != null) { String attr = model.getSelectedAttribute(); model.getInputConfig().setMaximumGeneralization(attr, null); model.getInputConfig().setMinimumGeneralization(attr, null); model.getInputConfig().setHierarchy(attr, hierarchy); update(new ModelEvent(this, ModelPart.HIERARCHY, hierarchy)); } } } /** * File->New project. */ public void actionMenuFileNew() { if ((model != null) && model.isModified()) { if (main.showQuestionDialog(main.getShell(), Resources.getMessage("Controller.61"), //$NON-NLS-1$ Resources.getMessage("Controller.62"))) { //$NON-NLS-1$ actionMenuFileSave(); } } // Separator final DialogProject dialog = new DialogProject(main.getShell()); dialog.create(); if (dialog.open() != Window.OK) { return; } // Set project reset(); model = dialog.getProject(); update(new ModelEvent(this, ModelPart.MODEL, model)); } /** * File->Open project. */ public void actionMenuFileOpen() { // Check if ((model != null) && model.isModified()) { if (main.showQuestionDialog(main.getShell(), Resources.getMessage("Controller.63"), //$NON-NLS-1$ Resources.getMessage("Controller.64"))) { //$NON-NLS-1$ actionMenuFileSave(); } } // Check final String path = actionShowOpenFileDialog(main.getShell(), "*.deid"); //$NON-NLS-1$ if (path == null) { return; } // Reset reset(); // Load actionOpenProject(path); } /** * File->Save project. */ public void actionMenuFileSave() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.66"), //$NON-NLS-1$ Resources.getMessage("Controller.67")); //$NON-NLS-1$ return; } if (model.getPath() == null) { actionMenuFileSaveAs(); } else { actionSaveProject(); } } /** * File->Save project as. */ public void actionMenuFileSaveAs() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.68"), //$NON-NLS-1$ Resources.getMessage("Controller.69")); //$NON-NLS-1$ return; } // Check String path = actionShowSaveFileDialog(main.getShell(), "*.deid"); //$NON-NLS-1$ if (path == null) { return; } if (!path.endsWith(".deid")) { //$NON-NLS-1$ path += ".deid"; //$NON-NLS-1$ } model.setPath(path); actionSaveProject(); } /** * Shows the "about" dialog. */ public void actionMenuHelpAbout() { main.showAboutDialog(); } /** * Shows the "debug" dialog. */ public void actionMenuHelpDebug() { if (model != null && model.isDebugEnabled()) main.showDebugDialog(); } /** * Shows the "help" dialog. */ public void actionMenuHelpHelp() { actionShowHelpDialog(null); } /** * Internal method for loading a project. * * @param path */ public void actionOpenProject(String path) { if (!path.endsWith(".deid")) { //$NON-NLS-1$ path += ".deid"; //$NON-NLS-1$ } WorkerLoad worker = null; try { worker = new WorkerLoad(path, this); } catch (final IOException e) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.82"), e.getMessage()); //$NON-NLS-1$ return; } main.showProgressDialog(Resources.getMessage("Controller.83"), worker); //$NON-NLS-1$ if (worker.getError() != null) { Throwable t = worker.getError(); if (worker.getError() instanceof InvocationTargetException) { t = worker.getError().getCause(); } String message = t.getMessage(); if (message == null || message.equals("")) { //$NON-NLS-1$ message = Resources.getMessage("Controller.46") + t.getClass().getSimpleName(); //$NON-NLS-1$ } getResources().getLogger().info(worker.getError()); main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.85"), //$NON-NLS-1$ message); return; } // Reset the workbench reset(); // Obtain the result model = worker.getResult(); model.setPath(path); // Temporary store parts of the model, because it might be overwritten // when updating the workbench final ModelNodeFilter tempNodeFilter = model.getNodeFilter(); final String tempSelectedAttribute = model.getSelectedAttribute(); final ARXNode tempSelectedNode = model.getSelectedNode(); final List<ARXNode> tempClipboard = model.getClipboard().getClipboardEntries(); // Update the model update(new ModelEvent(this, ModelPart.MODEL, model)); // Update subsets of the model if (model.getInputConfig().getInput() != null) { update(new ModelEvent(this, ModelPart.INPUT, model.getInputConfig().getInput().getHandle())); } // Update subsets of the model if (model.getResult() != null) { update(new ModelEvent(this, ModelPart.RESULT, model.getResult())); } // Update subsets of the model if (tempNodeFilter != null) { model.setNodeFilter(tempNodeFilter); update(new ModelEvent(this, ModelPart.FILTER, tempNodeFilter)); } // Update hierarchies and selected attribute if (model.getInputConfig() != null && model.getInputConfig().getInput() != null) { DataHandle handle = model.getInputConfig().getInput().getHandle(); if (handle != null) { for (int i = 0; i < handle.getNumColumns(); i++) { String attr = handle.getAttributeName(i); Hierarchy hierarchy = model.getInputConfig().getHierarchy(attr); if (hierarchy != null) { model.setSelectedAttribute(attr); update(new ModelEvent(this, ModelPart.HIERARCHY, hierarchy)); } } if (handle.getNumColumns() > 0) { String attribute = handle.getAttributeName(0); model.setSelectedAttribute(attribute); update(new ModelEvent(this, ModelPart.SELECTED_ATTRIBUTE, attribute)); } } } // Update subsets of the model if (tempSelectedAttribute != null) { model.setSelectedAttribute(tempSelectedAttribute); update(new ModelEvent(this, ModelPart.SELECTED_ATTRIBUTE, tempSelectedAttribute)); } // Update subsets of the model if (tempClipboard != null) { model.getClipboard().clearClipboard(); if (tempClipboard.isEmpty() && model.getResult() != null) { model.getClipboard().addInterestingTransformations(model); } else { model.getClipboard().addAllToClipboard(tempClipboard); } update(new ModelEvent(this, ModelPart.CLIPBOARD, model.getClipboard().getClipboardEntries())); } // Update the attribute types if (model.getInputConfig().getInput() != null) { final DataHandle handle = model.getInputConfig() .getInput() .getHandle(); for (int i = 0; i < handle.getNumColumns(); i++) { update(new ModelEvent(this, ModelPart.ATTRIBUTE_TYPE, handle.getAttributeName(i))); } } // Update research subset update(new ModelEvent(this, ModelPart.RESEARCH_SUBSET, model.getInputConfig().getResearchSubset())); // Update view config if (model.getOutput() != null) { update(new ModelEvent(this, ModelPart.SELECTED_VIEW_CONFIG, model.getOutput())); } // Update subsets of the model if (tempSelectedNode != null) { this.model.setSelectedNode(tempSelectedNode); this.update(new ModelEvent(this, ModelPart.SELECTED_NODE, model.getSelectedNode())); this.actionApplySelectedTransformation(); } // We just loaded the model, so there are no changes model.setUnmodified(); } /** * Shows the audit trail */ public void actionShowAuditTrail() { main.showAuditTrail(model.getAuditTrail()); } /** * Shows an input dialog for selecting a charset. * @param shell * @return */ public Charset actionShowCharsetInputDialog(final Shell shell) { return main.showCharsetInputDialog(shell); } /** * Shows an error dialog. * * @param shell * @param text * @param t */ public void actionShowErrorDialog(final Shell shell, final String text, final Throwable t) { main.showErrorDialog(shell, text, t); } /** * Shows a dialog for selecting a format string for a data type. * * @param shell The parent shell * @param title The dialog's title * @param text The dialog's text * @param locale The locale * @param type The description of the data type for which to choose a format string * @param values The values to check the format string against * @return The format string, or <code>null</code> if no format was (or could be) selected */ public String actionShowFormatInputDialog(final Shell shell, final String title, final String text, final Locale locale, final DataTypeDescription<?> type, final Collection<String> values) { return main.showFormatInputDialog(shell, title, text, null, locale, type, values); } /** * Shows a dialog for selecting a format string for a data type. * * @param shell The parent shell * @param title The dialog's title * @param text The dialog's text * @param locale The locale * @param type The description of the data type for which to choose a format string * @param values The values to check the format string against * @return The format string, or <code>null</code> if no format was (or could be) selected */ public String actionShowFormatInputDialog(final Shell shell, final String title, final String text, final Locale locale, final DataTypeDescription<?> type, final String[] values) { return main.showFormatInputDialog(shell, title, text, null, locale, type, Arrays.asList(values)); } /** * Shows a dialog for selecting a format string for a data type. * * @param shell The parent shell * @param title The dialog's title * @param text The dialog's text * @param preselected A preselected format string * @param locale The locale * @param type The description of the data type for which to choose a format string * @param values The values to check the format string against * @return The format string, or <code>null</code> if no format was (or could be) selected */ public String actionShowFormatInputDialog(final Shell shell, final String title, final String text, final String preselected, final Locale locale, final DataTypeDescription<?> type, final Collection<String> values) { return main.showFormatInputDialog(shell, title, text, preselected, locale, type, values); } /** * Shows a help dialog. * * @param id */ public void actionShowHelpDialog(String id) { main.showHelpDialog(id); } /** * Shows an info dialog. * * @param shell * @param header * @param text */ public void actionShowInfoDialog(final Shell shell, final String header, final String text) { main.showInfoDialog(shell, header, text); } /** * Shows an input dialog. * * @param shell * @param header * @param text * @param initial * @return */ public String actionShowInputDialog(final Shell shell, final String header, final String text, final String initial) { return main.showInputDialog(shell, header, text, initial); } /** * Shows an input dialog. * * @param shell * @param header * @param text * @param initial * @return */ public String actionShowInputDialog(final Shell shell, final String header, final String text, final String initial, final IInputValidator validator) { return main.showInputDialog(shell, header, text, initial, validator); } /** * Shows a dialog for selecting multiple elements * @param shell * @param title * @param text * @param elements * @param selected * @return */ public List<String> actionShowMultiSelectionDialog(Shell shell, String title, String text, List<String> elements, List<String> selected) { return main.showMultiSelectionDialog(shell, title, text, elements, selected); } /** * Shows a "open file" dialog. * * @param shell * @param filter * @return */ public String actionShowOpenFileDialog(final Shell shell, String filter) { return main.showOpenFileDialog(shell, filter); } /** * Shows an input dialog for ordering data items. * * @param shell * @param title The dialog's title * @param text The dialog's text * @param type The data type * @param locale * @param values The values * @return */ public String[] actionShowOrderValuesDialog(final Shell shell, final String title, final String text, final DataType<?> type, final Locale locale, final String[] values) { return main.showOrderValuesDialog(shell, title, text, type, locale, values); } /** * Shows a progress dialog. * * @param text * @param worker */ public void actionShowProgressDialog(final String text, final Worker<?> worker) { main.showProgressDialog(text, worker); } /** * Shows a question dialog. * * @param shell * @param header * @param text * @return */ public boolean actionShowQuestionDialog(final Shell shell, final String header, final String text) { return main.showQuestionDialog(shell, header, text); } /** * Shows a question dialog. * * @param header * @param text * @return */ public boolean actionShowQuestionDialog(final String header, final String text) { return main.showQuestionDialog(this.main.getShell(), header, text); } /** * Internal method for showing a "save file" dialog. * * @param shell * @param filter * @return */ public String actionShowSaveFileDialog(final Shell shell, String filter) { return main.showSaveFileDialog(shell, filter); } /** * Includes all tuples in the research subset. */ public void actionSubsetAll() { Data data = model.getInputConfig().getInput(); RowSet set = model.getInputConfig().getResearchSubset(); for (int i = 0; i < data.getHandle().getNumRows(); i++) { set.add(i); } model.setSubsetOrigin(Resources.getMessage("Controller.47")); //$NON-NLS-1$ update(new ModelEvent(this, ModelPart.RESEARCH_SUBSET, set)); } /** * Creates a research subset from a file. */ public void actionSubsetFile() { // Open wizard ARXWizard<ImportConfiguration> wizard = new ImportWizard(this, model); if (!wizard.open(main.getShell())) { return; } ImportConfiguration config = wizard.getResult(); if (config == null) { return; } final WorkerImport worker = new WorkerImport(config); main.showProgressDialog(Resources.getMessage("Controller.74"), worker); //$NON-NLS-1$ if (worker.getError() != null) { if (worker.getError() instanceof IllegalArgumentException) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.48"), //$NON-NLS-1$ worker.getError().getMessage()); } else { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.76"), //$NON-NLS-1$ worker.getError()); } return; } Data subsetData = worker.getResult(); Data data = model.getInputConfig().getInput(); try { DataSubset subset = DataSubset.create(data, subsetData); model.getInputConfig().setResearchSubset(subset.getSet()); model.setSubsetOrigin(Resources.getMessage("Controller.53")); //$NON-NLS-1$ update(new ModelEvent(this, ModelPart.RESEARCH_SUBSET, subset.getSet())); } catch (IllegalArgumentException e) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.60"), e.getMessage()); //$NON-NLS-1$ } } /** * Excludes all tuples from the subset. */ public void actionSubsetNone() { Data data = model.getInputConfig().getInput(); RowSet empty = RowSet.create(data); model.getInputConfig().setResearchSubset(empty); model.setSubsetOrigin(Resources.getMessage("Controller.65")); //$NON-NLS-1$ update(new ModelEvent(this, ModelPart.RESEARCH_SUBSET, empty)); } /** * Creates a subset by executing a query. */ public void actionSubsetQuery() { DialogQueryResult result = main.showQueryDialog(model.getQuery(), model.getInputConfig().getInput()); if (result == null) return; Data data = model.getInputConfig().getInput(); DataSelector selector = result.selector; DataSubset subset = DataSubset.create(data, selector); this.model.getInputConfig().setResearchSubset(subset.getSet()); this.model.setQuery(result.query); model.setSubsetOrigin(Resources.getMessage("Controller.70")); //$NON-NLS-1$ update(new ModelEvent(this, ModelPart.RESEARCH_SUBSET, subset.getSet())); } /** * Creates a subset via random sampling */ public void actionSubsetRandom() { String result = main.showInputDialog(main.getShell(), Resources.getMessage("Controller.130"), //$NON-NLS-1$ Resources.getMessage("Controller.131"), //$NON-NLS-1$ Resources.getMessage("Controller.132"), //$NON-NLS-1$ new IInputValidator() { @Override public String isValid(String arg0) { double value = 0d; try { value = Double.valueOf(arg0); } catch (Exception e) { return "Not a decimal"; } if (value < 0d || value > 1d) { return "Out of range"; } return null; } }); // Check if (result == null) { return; } // Convert double probability = Double.valueOf(result); // Create a data subset via sampling based on beta Set<Integer> subsetIndices = new HashSet<Integer>(); Random random = new SecureRandom(); int records = model.getInputConfig().getInput().getHandle().getNumRows(); for (int i = 0; i < records; ++i) { if (random.nextDouble() < probability) { subsetIndices.add(i); } } DataSubset subset = DataSubset.create(records, subsetIndices); this.model.getInputConfig().setResearchSubset(subset.getSet()); model.setSubsetOrigin(Resources.getMessage("Controller.133")); //$NON-NLS-1$ update(new ModelEvent(this, ModelPart.RESEARCH_SUBSET, subset.getSet())); } /** * Registers a listener at the controller. * * @param target * @param listener */ public void addListener(final ModelPart target, final IView listener) { if (!listeners.containsKey(target)) { listeners.put(target, new HashSet<IView>()); } listeners.get(target).add(listener); } @Override public void dispose() { for (final Set<IView> listeners : getListeners().values()) { for (final IView listener : listeners) { listener.dispose(); } } } /** * Returns debug data. * * @return */ public String getDebugData() { return this.debug.getData(model); } /** * Returns the current model * @return */ public Model getModel() { return model; } /** * Returns the resources. * * @return */ public Resources getResources() { // TODO: Move resources from controller to view? return resources; } /** * Unregisters a listener. * * @param listener */ public void removeListener(final IView listener) { for (final Set<IView> listeners : this.listeners.values()) { listeners.remove(listener); } } @Override public void reset() { for (final Set<IView> listeners : getListeners().values()) { for (final IView listener : listeners) { listener.reset(); } } } @Override public void update(final ModelEvent event) { if (model != null && model.isDebugEnabled()) this.debug.addEvent(event); final Map<ModelPart, Set<IView>> dlisteners = getListeners(); if (dlisteners.get(event.part) != null) { for (final IView listener : dlisteners.get(event.part)) { if (listener != event.source) { listener.update(event); } } } } /** * Internal method for importing data. * * @param config */ private void actionImportData(ImportConfiguration config) { final WorkerImport worker = new WorkerImport(config); main.showProgressDialog(Resources.getMessage("Controller.74"), worker); //$NON-NLS-1$ if (worker.getError() != null) { Throwable error = worker.getError(); if (error instanceof RuntimeException) { if (error.getCause() != null) { error = error.getCause(); } } if ((error instanceof IllegalArgumentException) || (error instanceof IOException)) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.71"), error.getMessage()); //$NON-NLS-1$ } else { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.76"), error); //$NON-NLS-1$ } return; } // Reset reset(); final Data data = worker.getResult(); if (model.getOutput() != null) { this.actionMenuEditReset(); } model.reset(); // Disable visualization if (model.getMaximalSizeForComplexOperations() > 0 && data.getHandle().getNumRows() > model.getMaximalSizeForComplexOperations()) { model.setVisualizationEnabled(false); } // Create a research subset containing all rows RowSet subset = RowSet.create(data); for (int i = 0; i < subset.length(); i++) { subset.add(i); } model.getInputConfig().setResearchSubset(subset); model.getInputConfig().setInput(data); // Nothing to fix if (config instanceof ImportConfigurationCSV) { ImportConfigurationCSV csvconfig = (ImportConfigurationCSV) config; model.setInputBytes(new File((csvconfig).getFileLocation()).length()); model.getCSVSyntax().setDelimiter(csvconfig.getDelimiter()); model.getCSVSyntax().setEscape(csvconfig.getEscape()); model.getCSVSyntax().setLinebreak(csvconfig.getLinebreak()); model.getCSVSyntax().setQuote(csvconfig.getQuote()); } else { model.setInputBytes(0); } // Create definition final DataDefinition definition = model.getInputDefinition(); for (int i = 0; i < data.getHandle().getNumColumns(); i++) { definition.setAttributeType(model.getInputConfig() .getInput() .getHandle() .getAttributeName(i), AttributeType.INSENSITIVE_ATTRIBUTE); } model.resetCriteria(); model.setGroups(null); model.setOutput(null, null); model.setViewConfig(new ModelViewConfig()); // Display the changes update(new ModelEvent(this, ModelPart.MODEL, model)); update(new ModelEvent(this, ModelPart.INPUT, data.getHandle())); if (data.getHandle().getNumColumns() > 0) { model.setSelectedAttribute(data.getHandle().getAttributeName(0)); update(new ModelEvent(this, ModelPart.SELECTED_ATTRIBUTE, data.getHandle().getAttributeName(0))); update(new ModelEvent(this, ModelPart.CRITERION_DEFINITION, null)); update(new ModelEvent(this, ModelPart.RESEARCH_SUBSET, subset)); } } /** * Internal method for importing hierarchies. * * @param path * @param separator * @return */ private Hierarchy actionImportHierarchy(final String path, final Charset charset, final char separator) { try { return Hierarchy.create(path, charset, separator); } catch (Throwable error) { if (error instanceof RuntimeException) { if (error.getCause() != null) { error = error.getCause(); } } if ((error instanceof IllegalArgumentException) || (error instanceof IOException)) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.72"), error.getMessage()); //$NON-NLS-1$ } else { main.showErrorDialog(main.getShell(), Resources.getMessage("Controller.78"), error); //$NON-NLS-1$ } } return null; } /** * Internal method for saving a project. */ private void actionSaveProject() { if (model == null) { main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.86"), //$NON-NLS-1$ Resources.getMessage("Controller.87")); //$NON-NLS-1$ return; } final WorkerSave worker = new WorkerSave(model.getPath(), this, model); main.showProgressDialog(Resources.getMessage("Controller.88"), worker); //$NON-NLS-1$ if (worker.getError() != null) { getResources().getLogger().info(worker.getError()); main.showInfoDialog(main.getShell(), Resources.getMessage("Controller.90"), //$NON-NLS-1$ worker.getError().getMessage()); return; } else { model.setUnmodified(); } } /** * Creates a deep copy of the listeners, to avoid concurrent modification * issues during updates of the model. * * @return */ private Map<ModelPart, Set<IView>> getListeners() { final Map<ModelPart, Set<IView>> result = new HashMap<ModelPart, Set<IView>>(); for (final ModelPart key : listeners.keySet()) { result.put(key, new HashSet<IView>()); result.get(key).addAll(listeners.get(key)); } return result; } /** * Updates the view config. * * @param force Force update even if unchanged */ private void updateViewConfig(boolean force) { ModelViewConfig config = model.getViewConfig(); if (!force && !config.isChanged()) return; DataHandle handle = (config.getMode() == Mode.SORTED_INPUT) ? model.getInputConfig().getInput().getHandle() : model.getOutput(); handle = config.isSubset() ? handle.getView() : handle; if (config.getMode() == Mode.UNSORTED) { model.setGroups(null); } else if (config.getMode() == Mode.SORTED_INPUT || config.getMode() == Mode.SORTED_OUTPUT) { // Sort Swapper swapper = new Swapper() { @Override public void swap(int arg0, int arg1) { model.getInputConfig().getResearchSubset().swap(arg0, arg1); } }; handle.sort(swapper, config.getSortOrder(), handle.getColumnIndexOf(config.getAttribute())); model.setGroups(null); } else { // Groups // Create array with indices of all QIs DataDefinition definition = model.getOutputDefinition(); int[] indices = new int[definition.getQuasiIdentifyingAttributes().size()]; int index = 0; for (String attribute : definition.getQuasiIdentifyingAttributes()) { indices[index++] = handle.getColumnIndexOf(attribute); } // Sort by all QIs Swapper swapper = new Swapper() { @Override public void swap(int arg0, int arg1) { model.getInputConfig().getResearchSubset().swap(arg0, arg1); } }; handle.sort(swapper, true, indices); // Identify groups int[] groups = new int[handle.getNumRows()]; int groupIdx = 0; groups[0] = 0; // For each row for (int row = 1; row < handle.getNumRows(); row++) { // Check if different from previous boolean newClass = false; for (int column : indices) { if (!handle.getValue(row, column).equals(handle.getValue(row - 1, column))) { newClass = true; break; } } // Store group groupIdx += newClass ? 1 : 0; groups[row] = groupIdx; } // Update model.setGroups(groups); } } }