/* * 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.wizard; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.List; import org.deidentifier.arx.DataType; import org.deidentifier.arx.DataType.DataTypeWithRatioScale; import org.deidentifier.arx.aggregates.AggregateFunction; import org.deidentifier.arx.aggregates.HierarchyBuilderGroupingBased.Group; import org.deidentifier.arx.aggregates.HierarchyBuilderGroupingBased.Level; import org.deidentifier.arx.aggregates.HierarchyBuilderIntervalBased; import org.deidentifier.arx.aggregates.HierarchyBuilderIntervalBased.Interval; import org.deidentifier.arx.aggregates.HierarchyBuilderIntervalBased.Range; import org.deidentifier.arx.aggregates.HierarchyBuilderOrderBased; import org.deidentifier.arx.gui.resources.Resources; import org.deidentifier.arx.gui.view.impl.wizard.HierarchyWizard.HierarchyWizardView; /** * A base-class for grouping-based builders, i.e., order-based and interval-based builders * @author Fabian Prasser * * @param <T> */ public abstract class HierarchyWizardModelGrouping<T> extends HierarchyWizardModelAbstract<T>{ /** * This class represents a group. * * @author Fabian Prasser * @param <U> */ public static class HierarchyWizardGroupingGroup<U> { /** Var. */ public int size; /** Var. */ public AggregateFunction<U> function; /** * * * @param group */ public HierarchyWizardGroupingGroup(Group<U> group) { this.size = group.getSize(); this.function = group.getFunction(); } /** * * * @param size * @param function */ public HierarchyWizardGroupingGroup(int size, AggregateFunction<U> function) { this.size = size; this.function = function; } } /** * This class represents an interval. * * @author Fabian Prasser * @param <U> */ public static class HierarchyWizardGroupingInterval<U> { /** TODO */ public U min; /** TODO */ public U max; /** TODO */ public AggregateFunction<U> function; /** * * * @param interval */ public HierarchyWizardGroupingInterval(Interval<U> interval) { this.min = interval.getMin(); this.max = interval.getMax(); this.function = interval.getFunction(); } /** * * * @param min * @param max * @param function */ public HierarchyWizardGroupingInterval(U min, U max, AggregateFunction<U> function) { this.min = min; this.max = max; this.function = function; } } /** * This class represents an adjustment. * * @author Fabian Prasser * @param <U> */ public static class HierarchyWizardGroupingRange<U> { /** TODO */ public U repeat; /** TODO */ public U snap; /** TODO */ public U label; /** * * * @param type * @param lower */ @SuppressWarnings("unchecked") public HierarchyWizardGroupingRange(DataType<U> type, boolean lower){ DataTypeWithRatioScale<U> dtype = (DataTypeWithRatioScale<U>)type; if (lower){ this.repeat = dtype.getMinimum(); this.snap = dtype.getMinimum(); this.label = dtype.getMinimum(); } else { this.repeat = dtype.getMaximum(); this.snap = dtype.getMaximum(); this.label = dtype.getMaximum(); } } /** * * * @param range */ public HierarchyWizardGroupingRange(Range<U> range) { this.repeat = range.getRepeatBound(); this.snap = range.getSnapBound(); this.label = range.getLabelBound(); } /** * * * @param repeat * @param snap * @param label */ public HierarchyWizardGroupingRange(U repeat, U snap, U label) { this.repeat = repeat; this.snap = snap; this.label = label; } } /** Var. */ private List<HierarchyWizardGroupingInterval<T>> intervals = new ArrayList<HierarchyWizardGroupingInterval<T>>(); /** Var. */ private List<List<HierarchyWizardGroupingGroup<T>>> groups = new ArrayList<List<HierarchyWizardGroupingGroup<T>>>(); /** Var. */ private DataType<T> type; /** Var. */ private boolean showIntervals = true; /** Var. */ private AggregateFunction<T> function; /** Var. */ private HierarchyWizardGroupingRange<T> lower = null; /** Var. */ private HierarchyWizardGroupingRange<T> upper = null; /** Var. */ private Object selected = null; /** Var. */ private HierarchyWizardEditorRenderer<T> renderer = new HierarchyWizardEditorRenderer<T>(this); /** Var. */ private List<HierarchyWizardView> components = new ArrayList<HierarchyWizardView>(); /** * Creates a new instance. * * @param data * @param type * @param intervals */ @SuppressWarnings("unchecked") public HierarchyWizardModelGrouping(String[] data, DataType<T> type, boolean intervals){ super(data); this.type = type; this.showIntervals = intervals; if (intervals) { // Check if (!(type instanceof DataTypeWithRatioScale)) { throw new IllegalArgumentException(Resources.getMessage("HierarchyWizardModelGrouping.0")); //$NON-NLS-1$ } // Prepare DataTypeWithRatioScale<T> dtype = (DataTypeWithRatioScale<T>)type; this.lower = new HierarchyWizardGroupingRange<T>(type, true); this.upper = new HierarchyWizardGroupingRange<T>(type, false); this.function = AggregateFunction.forType(type).createIntervalFunction(true, false); // Initialize if (data != null){ T min = null; T max = null; for (String date : data) { T value = dtype.parse(date); if (value != null) { if (min==null || dtype.compare(value, min) < 0) min = value; if (max==null || dtype.compare(value, max) > 0) max = value; } } if (equals(type, DataType.INTEGER)) { max = dtype.add(max, (T)new Long(1)); // Add 1 } else if (equals(type, DataType.DECIMAL)) { max = dtype.add(max, (T)new Double(1)); // Add 1 } else if (equals(type, DataType.DATE)) { max = dtype.add(max, (T)new Date(3600l * 1000l)); // Add 1 day } this.lower.label = min; this.lower.repeat = min; this.lower.snap = min; this.upper.label = max; this.upper.repeat = max; this.upper.snap = max; this.intervals.add(new HierarchyWizardGroupingInterval<T>(min, max, this.function)); } else { this.intervals.add(new HierarchyWizardGroupingInterval<T>(dtype.getMinimum(), dtype.getMaximum(), this.function)); } } else { this.function = AggregateFunction.forType(type).createSetFunction(); this.groups.add(new ArrayList<HierarchyWizardGroupingGroup<T>>()); this.groups.get(0).add(new HierarchyWizardGroupingGroup<T>(1, this.function)); } this.update(); } /** * Adds an element after the given one. * * @param selected */ @SuppressWarnings("unchecked") public void addAfter(Object selected) { if (selected instanceof HierarchyWizardGroupingInterval){ int index= intervals.indexOf(selected); if (index != -1){ T bound = ((HierarchyWizardGroupingInterval<T>)selected).max; intervals.add(index + 1, new HierarchyWizardGroupingInterval<T>(bound, bound, this.function)); update(); return; } } else if (selected instanceof HierarchyWizardGroupingGroup){ for (List<HierarchyWizardGroupingGroup<T>> list : groups){ int index= list.indexOf(selected); if (index != -1){ list.add(index + 1, new HierarchyWizardGroupingGroup<T>(1, this.function)); update(); return; } } } } /** * Adds an element before the given one. * * @param selected */ @SuppressWarnings("unchecked") public void addBefore(Object selected) { if (selected instanceof HierarchyWizardGroupingInterval){ int index= intervals.indexOf(selected); if (index != -1){ T bound = ((HierarchyWizardGroupingInterval<T>)selected).min; intervals.add(index, new HierarchyWizardGroupingInterval<T>(bound, bound, this.function)); update(); return; } } else if (selected instanceof HierarchyWizardGroupingGroup){ for (List<HierarchyWizardGroupingGroup<T>> list : groups){ int index= list.indexOf(selected); if (index != -1){ list.add(index, new HierarchyWizardGroupingGroup<T>(1, this.function)); update(); return; } } } } /** * Adds groups. * * @param list */ public void addGroups(List<HierarchyWizardGroupingGroup<T>> list) { groups.add(list); } /** * Adds an interval. * * @param i */ public void addInterval(HierarchyWizardGroupingInterval<T> i) { intervals.add(i); } /** * Adds a column. * * @param selected */ public void addRight(Object selected) { int index = 0; if (selected instanceof HierarchyWizardGroupingGroup){ for (int i=0; i<groups.size(); i++){ if (groups.get(i).indexOf(selected) != -1) { index = i+1; } } } List<HierarchyWizardGroupingGroup<T>> list = new ArrayList<HierarchyWizardGroupingGroup<T>>(); groups.add(index, list); list.add(new HierarchyWizardGroupingGroup<T>(1, function)); update(); } /** * @return the type */ public DataType<T> getDataType() { return type; } /** * Returns the default aggregate function. * * @return */ public AggregateFunction<T> getDefaultFunction(){ return this.function; } /** * @return the intervals */ public List<HierarchyWizardGroupingInterval<T>> getIntervals() { return intervals; } /** * @return the lower */ public HierarchyWizardGroupingRange<T> getLowerRange() { return lower; } /** * @return the groups */ public List<List<HierarchyWizardGroupingGroup<T>>> getModelGroups() { return groups; } /** * @return the renderer */ public HierarchyWizardEditorRenderer<T> getRenderer() { return renderer; } /** * @return the selected */ public Object getSelectedElement() { return selected; } /** * @return the upper */ public HierarchyWizardGroupingRange<T> getUpperRange() { return upper; } /** * Is this the first interval. * * @param interval * @return */ public boolean isFirst(HierarchyWizardGroupingInterval<T> interval){ return intervals.indexOf(interval) == 0; } /** * Is this the last interval. * * @param interval * @return */ public boolean isLast(HierarchyWizardGroupingInterval<T> interval){ return intervals.indexOf(interval) == intervals.size()-1; } /** * @return the showIntervals */ public boolean isShowIntervals() { return showIntervals; } /** * Merges the interval down. * * @param selected */ public void mergeDown(Object selected) { if (selected instanceof HierarchyWizardGroupingInterval){ int index = intervals.indexOf(selected); if (index != -1) { AggregateFunction<T> function = intervals.get(index).function; T min = intervals.get(index-1).min; T max = intervals.get(index).max; intervals.remove(index-1); intervals.remove(index-1); intervals.add(index-1, new HierarchyWizardGroupingInterval<T>(min, max, function)); update(); } } } /** * Merges the interval up. * * @param selected */ public void mergeUp(Object selected) { if (selected instanceof HierarchyWizardGroupingInterval){ int index = intervals.indexOf(selected); if (index != -1) { AggregateFunction<T> function = intervals.get(index).function; T min = intervals.get(index).min; T max = intervals.get(index+1).max; intervals.remove(index); intervals.remove(index); intervals.add(index, new HierarchyWizardGroupingInterval<T>(min, max, function)); update(); } } } /** * Parses an interval-based spec. * * @param builder */ public void parse(HierarchyBuilderIntervalBased<T> builder){ this.type = builder.getDataType(); this.showIntervals = true; this.lower.label = builder.getLowerRange().getLabelBound(); this.lower.repeat = builder.getLowerRange().getRepeatBound(); this.lower.snap = builder.getLowerRange().getSnapBound(); this.upper.label = builder.getUpperRange().getLabelBound(); this.upper.repeat = builder.getUpperRange().getRepeatBound(); this.upper.snap = builder.getUpperRange().getSnapBound(); this.function = builder.getDefaultFunction(); this.intervals.clear(); this.groups.clear(); for (Interval<T> interval : builder.getIntervals()) { this.intervals.add(new HierarchyWizardGroupingInterval<T>(interval)); } for (Level<T> level : builder.getLevels()){ List<HierarchyWizardGroupingGroup<T>> list = new ArrayList<HierarchyWizardGroupingGroup<T>>(); this.groups.add(list); for (Group<T> group : level.getGroups()) { list.add(new HierarchyWizardGroupingGroup<T>(group)); } } update(); } /** * Parses an order-based spec. * * @param builder * @throws IllegalArgumentException */ public void parse(HierarchyBuilderOrderBased<T> builder) throws IllegalArgumentException{ if (builder.getComparator() != null) { try { Arrays.sort(this.data, builder.getComparator()); } catch (Exception e){ throw new IllegalArgumentException(Resources.getMessage("HierarchyWizardModelGrouping.1")); //$NON-NLS-1$ } } this.showIntervals = false; this.function = builder.getDefaultFunction(); this.intervals.clear(); this.groups.clear(); this.lower = null; this.upper = null; for (Level<T> level : builder.getLevels()){ List<HierarchyWizardGroupingGroup<T>> list = new ArrayList<HierarchyWizardGroupingGroup<T>>(); this.groups.add(list); for (Group<T> group : level.getGroups()) { list.add(new HierarchyWizardGroupingGroup<T>(group)); } } update(); } /** * Registers a part of the UI. * * @param component */ public void register(HierarchyWizardView component){ this.components.add(component); } /** * Removes the given object. * * @param selected */ public void remove(Object selected) { if (selected instanceof HierarchyWizardGroupingInterval){ Iterator<HierarchyWizardGroupingInterval<T>> iter = intervals.iterator(); while (iter.hasNext()){ if (iter.next().equals(selected)) { iter.remove(); selected = null; update(); return; } } } else if (selected instanceof HierarchyWizardGroupingGroup){ for (List<HierarchyWizardGroupingGroup<T>> list : groups){ Iterator<HierarchyWizardGroupingGroup<T>> iter = list.iterator(); while (iter.hasNext()){ if (iter.next().equals(selected)) { iter.remove(); if (list.isEmpty()) { groups.remove(list); } selected = null; update(); return; } } } } } /** * Sets the default aggregate function. * * @param function */ public void setDefaultFunction(AggregateFunction<T> function){ AggregateFunction<T> old = this.function; this.function = function; // Update for (HierarchyWizardGroupingInterval<T> interval : intervals) { if (interval.function == old){ interval.function = function; } } for (List<HierarchyWizardGroupingGroup<T>> list : groups) { for (HierarchyWizardGroupingGroup<T> group : list) { if (group.function == old){ group.function = function; } } } } /** * Updates the selected element. * * @param selected */ public void setSelectedElement(Object selected) { this.selected = selected; } /** * Update the model and all UI components. */ @Override public void update(){ super.update(); renderer.update(); updateUI(null); } /** * Update the model and all UI components, apart from the sender. * * @param sender */ public void update(HierarchyWizardView sender){ super.update(); renderer.update(); updateUI(sender); } /** * Update the UI components. */ @Override public void updateUI(HierarchyWizardView sender){ for (HierarchyWizardView c : components){ if (c != sender) { c.update(); } } } /** * Simple comparison of data types. * * @param type * @param other * @return */ private boolean equals(DataType<?> type, DataType<?> other){ return type.getDescription().getLabel().equals(other.getDescription().getLabel()); } }