/* * 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.HashSet; import java.util.List; import java.util.Set; import org.deidentifier.arx.DataDefinition; import org.deidentifier.arx.DataHandle; 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.view.SWTUtil; import org.deidentifier.arx.gui.view.def.IView; import org.deidentifier.arx.gui.view.impl.common.DelayedChangeListener; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import de.linearbits.swt.widgets.Knob; import de.linearbits.swt.widgets.KnobColorProfile; import de.linearbits.swt.widgets.KnobRange; /** * This class allows to define weights for attributes. * * @author Fabian Prasser */ public class ViewAttributeWeights implements IView { /** Constant */ private static final int MIN_SPACE = 60; /** Constant */ private static final int MIN_KNOB = 30; /** Controller. */ private Controller controller = null; /** Model. */ private Model model = null; /** Model. */ private final Set<String> attributes = new HashSet<String>(); /** View. */ private Composite panel = null; /** View. */ private final ScrolledComposite root; /** Color profile */ private final KnobColorProfile defaultColorProfile; /** Color profile */ private final KnobColorProfile focusedColorProfile; /** * Creates a new instance. * * @param parent * @param controller */ public ViewAttributeWeights(final Composite parent, final Controller controller) { // Register this.controller = controller; this.controller.addListener(ModelPart.ATTRIBUTE_TYPE, this); this.controller.addListener(ModelPart.MODEL, this); this.controller.addListener(ModelPart.INPUT, this); // Color profiles this.defaultColorProfile = KnobColorProfile.createDefaultSystemProfile(parent.getDisplay()); this.focusedColorProfile = KnobColorProfile.createFocusedBlueRedProfile(parent.getDisplay()); this.root = new ScrolledComposite(parent, SWT.H_SCROLL); this.root.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent arg0) { if (defaultColorProfile != null && !defaultColorProfile.isDisposed()) { defaultColorProfile.dispose(); } if (focusedColorProfile != null && !focusedColorProfile.isDisposed()) { focusedColorProfile.dispose(); } } }); this.root.getHorizontalBar().addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent arg0) { root.redraw(); } }); root.pack(); } @Override public void dispose() { controller.removeListener(this); root.dispose(); } @Override public void reset() { if (root.isDisposed()) { return; } root.setRedraw(false); if (panel != null) { panel.dispose(); panel = null; } attributes.clear(); root.setRedraw(true); } @Override public void update(ModelEvent event) { if (event.part == ModelPart.MODEL) { this.model = (Model)event.data; } if (event.part == ModelPart.MODEL || event.part == ModelPart.INPUT) { this.attributes.clear(); } if (event.part == ModelPart.ATTRIBUTE_TYPE || event.part == ModelPart.MODEL) { if (model!=null) { updateControls(); } } } /** * Updates the controls */ private void updateControls() { // Create ordered list of QIs DataDefinition definition = model.getInputDefinition(); List<String> qis = new ArrayList<String>(); if (definition != null) { Set<String> _qis = definition.getQuasiIdentifyingAttributes(); // Break if nothing has changed if (this.attributes.equals(_qis)) { return; } DataHandle handle = model.getInputConfig().getInput().getHandle(); for (int i=0; i<handle.getNumColumns(); i++){ String attr = handle.getAttributeName(i); if (_qis.contains(attr)){ qis.add(attr); } } attributes.clear(); attributes.addAll(qis); } if (root.isDisposed()) return; root.setRedraw(false); // Dispose widgets if (panel != null) { panel.dispose(); } // Create layout panel = new Composite(root, SWT.NONE); panel.setLayoutData(GridDataFactory.swtDefaults().grab(true, true).align(SWT.FILL, SWT.CENTER).create()); panel.setLayout(GridLayoutFactory.swtDefaults().numColumns(qis.size()).margins(0, 0).equalWidth(true).create()); // Create composites List<Composite> composites = new ArrayList<Composite>(); for(int i=0; i<qis.size(); i++){ Composite c = new Composite(panel, SWT.NONE); c.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.CENTER).create()); c.setLayout(GridLayoutFactory.swtDefaults().numColumns(1).margins(2, 0).create()); composites.add(c); } // Create labels for(int i=0; i<qis.size(); i++){ Label label = new Label(composites.get(i), SWT.CENTER); label.setText(qis.get(i)); label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1)); } // Create knob widgets List<Knob<Double>> knobs = new ArrayList<Knob<Double>>(); for(int i=0; i<qis.size(); i++){ Knob<Double> knob = new Knob<Double>(composites.get(i), SWT.NULL, new KnobRange.Double(0d, 1d)); knob.setLayoutData(GridDataFactory.swtDefaults().grab(false, false).align(SWT.CENTER, SWT.CENTER).hint(MIN_KNOB, MIN_KNOB).create()); knob.setDefaultColorProfile(defaultColorProfile); knob.setFocusedColorProfile(focusedColorProfile); knobs.add(knob); } // Create labels for(int i=0; i<qis.size(); i++){ final Label label = new Label(composites.get(i), SWT.CENTER); label.setText("0.0"); //$NON-NLS-1$ label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1)); final String attribute = qis.get(i); final Knob<Double> knob = knobs.get(i); knob.addSelectionListener(new SelectionAdapter(){ public void widgetSelected(SelectionEvent arg0) { double value = knob.getValue(); label.setText(SWTUtil.getPrettyString(value)); label.setToolTipText(String.valueOf(value)); try { // Correctly indicate weights slightly > 0 double parsedValue = Double.valueOf(SWTUtil.getPrettyString(value)).doubleValue(); if (parsedValue == 0d && value > 0d) { label.setText(">0"); //$NON-NLS-1$ } // Correctly indicate weights slightly < 1 if (parsedValue == 1d && value < 1d) { label.setText("<1"); //$NON-NLS-1$ } } catch (Exception e) { // Drop silently } if (model != null && model.getInputConfig() != null) { model.getInputConfig().setAttributeWeight(attribute, value); } } }); knob.addSelectionListener(new DelayedChangeListener(100) { @Override public void delayedEvent() { controller.update(new ModelEvent(ViewAttributeWeights.this, ModelPart.ATTRIBUTE_WEIGHT, model.getInputConfig().getAttributeWeight(attribute))); } }); } // Set values for(int i=0; i<qis.size(); i++){ if (model != null && model.getInputConfig() != null) { knobs.get(i).setValue(model.getInputConfig().getAttributeWeight(qis.get(i))); } } root.setContent(panel); root.setMinWidth(MIN_SPACE * qis.size()); root.setExpandHorizontal(true); root.setExpandVertical(true); // Update root composite root.setVisible(!qis.isEmpty()); root.layout(true, true); root.setRedraw(true); } }