/* * 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.framework.check; import org.deidentifier.arx.framework.check.history.History; /** * This class implements a state machine, which determines which optimizations * can be applied to the current transition depending on the previous * transition. * * @author Fabian Prasser * @author Florian Kohlmayer */ public class StateMachine { /** * The resulting transition. * * @author Fabian Prasser * @author Florian Kohlmayer */ public static class Transition { /** Which columns can be projected away?. */ public long projection; /** Snapshot, if available. */ public int[] snapshot; /** The actual type of the transition. */ public TransitionType type; }; /** * The possible transition type. * * @author Fabian Prasser * @author Florian Kohlmayer */ public static enum TransitionType { /** Apply the roll-up optimization. */ ROLLUP, /** Apply the snapshot optimization. */ SNAPSHOT, /** Unfortunately all rows need to be transformed. */ UNOPTIMIZED } /** The history. */ private History history = null; /** The last node, which has been checked for k-anonymity. */ private int[] lastNode; /** The last transition, which has been performed. */ private Transition lastTransition; /** The current snapshot, if any. */ private int[] snapshot = null; /** The node for the current snapshot. */ private int[] snapshotNode; /** * Instantiates a new state machine. * * @param history * the history */ public StateMachine(final History history) { this.lastNode = null; this.lastTransition = null; this.history = history; } /** * Returns the last node. * * @return the last node, which has been checked for k-anonymity */ public int[] getLastNode() { return lastNode; } /** * * * @return */ public Transition getLastTransition() { return lastTransition; } /** * Resets the state machine. */ public void reset() { lastNode = null; lastTransition = null; } /** * Computes the best state transition. * * @param currentNode * the current node * @return the transition */ public Transition transition(final int[] currentNode) { final Transition result = new Transition(); // First transition if (lastTransition == null) { result.type = TransitionType.UNOPTIMIZED; result.projection = 0L; result.snapshot = null; } else { switch (lastTransition.type) { case UNOPTIMIZED: result.projection = getProjection(currentNode); if (isPossibleSnapshot(currentNode)) { result.type = TransitionType.SNAPSHOT; result.snapshot = snapshot; } else if (isPossibleRollup(currentNode)) { result.type = TransitionType.ROLLUP; result.snapshot = null; } else { result.type = TransitionType.UNOPTIMIZED; result.snapshot = null; } break; case ROLLUP: case SNAPSHOT: if (isPossibleSnapshot(currentNode)) { result.projection = isPredecessor(snapshotNode, lastNode) ? getProjection(currentNode) : 0L; result.type = TransitionType.SNAPSHOT; result.snapshot = snapshot; } else if (isPossibleRollup(currentNode)) { result.projection = getProjection(currentNode); result.type = TransitionType.ROLLUP; result.snapshot = null; } else { result.projection = 0L; result.type = TransitionType.UNOPTIMIZED; result.snapshot = null; } break; } } // Store lastNode = currentNode; lastTransition = result; // Return return result; } /** * Returns the projection. All bits are set for the columns that don't need * to be checked * * @param currentNode * the current node * @return the projection */ private long getProjection(final int[] currentNode) { long projection = 0L; for (int i = 0; i < currentNode.length; i++) { if (currentNode[i] == lastNode[i]) { projection |= 1L << i; } } return projection; } /** * Is a rollup optimization possible. * * @param currentNode * the current node * @return true, if is possible rollup */ private boolean isPossibleRollup(final int[] currentNode) { for (int i = 0; i < lastNode.length; i++) { if (currentNode[i] < lastNode[i]) { return false; } } return true; } /** * Is a snapshot optimization possible. * * @param currentNode * the current node * @return true, if is possible snapshot */ private boolean isPossibleSnapshot(final int[] currentNode) { snapshot = history.get(currentNode); snapshotNode = history.getTransformation(); if (snapshot != null) { return true; } return false; } /** * Is node2 a predecessor of or equal to node1?. * * @param node1 * @param node2 * @return */ private boolean isPredecessor(final int[] node1, final int[] node2) { for (int i = 0; i < node2.length; i++) { if (node1[i] < node2[i]) { return false; } } return true; } }