/* * 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.lattice; import java.util.Arrays; import org.deidentifier.arx.framework.check.NodeChecker; import org.deidentifier.arx.metric.InformationLoss; import cern.colt.list.LongArrayList; import de.linearbits.jhpl.JHPLIterator.LongIterator; import de.linearbits.jhpl.Lattice; import de.linearbits.jhpl.PredictiveProperty; import de.linearbits.jhpl.PredictiveProperty.Direction; /** * The class Transformation. * * @author Fabian Prasser */ public class Transformation { /** The id. */ private final long identifier; /** The lattice */ private final Lattice<Integer, Integer> lattice; /** The level */ private int levelARX = -1; /** The level */ private int levelJHPL = -1; /** The solution space */ private final SolutionSpace solutionSpace; /** Transformation in ARX's space */ private int[] transformationARX = null; /** Transformation in JHPL's space */ private final int[] transformationJHPL; /** * Instantiates a new transformation. * @param transformation In ARX space * @param lattice * @param solutionSpace */ public Transformation(int[] transformation, Lattice<Integer, Integer> lattice, SolutionSpace solutionSpace) { this.lattice = lattice; this.solutionSpace = solutionSpace; this.transformationARX = transformation; this.transformationJHPL = solutionSpace.toJHPL(transformation); this.identifier = lattice.space().toId(transformationJHPL); } /** * Instantiates a new transformation. * @param transformationJHPL * @param identifier * @param lattice2 * @param solutionSpace2 */ public Transformation(int[] transformationJHPL, long identifier, Lattice<Integer, Integer> lattice, SolutionSpace solutionSpace) { this.lattice = lattice; this.solutionSpace = solutionSpace; this.transformationJHPL = transformationJHPL; this.identifier = identifier; } /** * Returns associated data * @return */ public Object getData() { return this.solutionSpace.getData(this.identifier); } /** * Returns the generalization * @return */ public int[] getGeneralization() { if (this.transformationARX == null) { this.transformationARX = solutionSpace.fromJHPL(transformationJHPL); } return this.transformationARX; } /** * Returns the id * @return */ public long getIdentifier() { return identifier; } /** * Returns the information loss * @return */ public InformationLoss<?> getInformationLoss() { return solutionSpace.getInformationLoss(this.identifier); } /** * Return level * @return */ public int getLevel() { if (this.levelARX == -1) { this.levelJHPL = getLevel(transformationJHPL); this.levelARX = solutionSpace.fromJHPL(levelJHPL); } return levelARX; } /** * Returns the lower bound on information loss * @return */ public InformationLoss<?> getLowerBound() { return solutionSpace.getLowerBound(this.identifier); } /** * Returns all predeccessors of the transformation with the given identifier * @param transformation * @return */ public LongArrayList getPredecessors() { LongArrayList result = new LongArrayList(); for (LongIterator iter = lattice.nodes().listPredecessorsAsIdentifiers(transformationJHPL, identifier); iter.hasNext();) { result.add(iter.next()); } return result; } /** * Returns all successors * @return */ public LongArrayList getSuccessors() { cern.colt.list.LongArrayList result = new cern.colt.list.LongArrayList(); for (LongIterator iter = lattice.nodes().listSuccessorsAsIdentifiers(transformationJHPL, identifier); iter.hasNext();) { result.add(iter.next()); } int lower = 0; int upper = result.size() - 1; while (lower < upper) { long temp = result.get(lower); result.set(lower, result.get(upper)); result.set(upper, temp); lower++; upper--; } return result; } /** * Returns whether this transformation has a given property * @param property * @return */ public boolean hasProperty(PredictiveProperty property) { getLevel(); return this.lattice.hasProperty(this.transformationJHPL, this.levelJHPL, property); } /** * Sets the properties to the given node. * * @param node the node * @param result the result */ public void setChecked(NodeChecker.Result result) { // Set checked this.setProperty(solutionSpace.getPropertyChecked()); // Anonymous if (result.privacyModelFulfilled){ this.setProperty(solutionSpace.getPropertyAnonymous()); } else { this.setProperty(solutionSpace.getPropertyNotAnonymous()); } // k-Anonymous if (result.minimalClassSizeFulfilled != null) { if (result.minimalClassSizeFulfilled){ this.setProperty(solutionSpace.getPropertyKAnonymous()); } else { this.setProperty(solutionSpace.getPropertyNotKAnonymous()); } } // Infoloss this.setInformationLoss(result.informationLoss); this.setLowerBound(result.lowerBound); } /** * Sets a data * @param object */ public void setData(Object object) { this.solutionSpace.setData(this.identifier, object); } /** * Sets the information loss * @param informationLoss */ public void setInformationLoss(InformationLoss<?> informationLoss) { this.solutionSpace.setInformationLoss(this.identifier, informationLoss); } /** * Sets the lower bound * @param lowerBound */ public void setLowerBound(InformationLoss<?> lowerBound) { this.solutionSpace.setLowerBound(this.identifier, lowerBound); } /** * Sets a property * @param property */ public void setProperty(PredictiveProperty property) { getLevel(); this.lattice.putProperty(this.transformationJHPL, this.levelJHPL, property); } /** * Sets the property to all neighbors * @param property */ public void setPropertyToNeighbours(PredictiveProperty property) { LongIterator neighbors; if (property.getDirection() == Direction.UP) { neighbors = lattice.nodes().listSuccessorsAsIdentifiers(transformationJHPL, identifier); } else if (property.getDirection() == Direction.DOWN) { neighbors = lattice.nodes().listPredecessorsAsIdentifiers(transformationJHPL, identifier); } else { return; } LongArrayList list = new LongArrayList(); for (;neighbors.hasNext();) { list.add(neighbors.next()); } for (int i=0; i<list.size(); i++) { int[] index = lattice.space().toIndex(list.getQuick(i)); int level = lattice.nodes().getLevel(index); lattice.putProperty(index, level, property); } } /** * Returns a string representation */ public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Transformation {\n"); builder.append(" - Solution space: ").append(this.solutionSpace.hashCode()).append("\n"); builder.append(" - Index: ").append(Arrays.toString(transformationJHPL)).append("\n"); builder.append(" - Id: ").append(identifier).append("\n"); builder.append(" - Generalization: ").append(Arrays.toString(getGeneralization())).append("\n"); builder.append(" - Level: ").append(getLevel()).append("\n"); builder.append(" - Properties:\n"); if (lattice.hasProperty(transformationJHPL, this.levelJHPL, solutionSpace.getPropertyAnonymous())) { builder.append(" * ANONYMOUS: ").append(solutionSpace.getPropertyAnonymous().getDirection()).append("\n"); } if (lattice.hasProperty(transformationJHPL, this.levelJHPL, solutionSpace.getPropertyNotAnonymous())) { builder.append(" * NOT_ANONYMOUS: ").append(solutionSpace.getPropertyNotAnonymous().getDirection()).append("\n"); } if (lattice.hasProperty(transformationJHPL, this.levelJHPL, solutionSpace.getPropertyKAnonymous())) { builder.append(" * K_ANONYMOUS: ").append(solutionSpace.getPropertyKAnonymous().getDirection()).append("\n"); } if (lattice.hasProperty(transformationJHPL, this.levelJHPL, solutionSpace.getPropertyNotKAnonymous())) { builder.append(" * NOT_K_ANONYMOUS: ").append(solutionSpace.getPropertyNotKAnonymous().getDirection()).append("\n"); } if (lattice.hasProperty(transformationJHPL, this.levelJHPL, solutionSpace.getPropertyChecked())) { builder.append(" * CHECKED: ").append(solutionSpace.getPropertyChecked().getDirection()).append("\n"); } if (lattice.hasProperty(transformationJHPL, this.levelJHPL, solutionSpace.getPropertyForceSnapshot())) { builder.append(" * FORCE_SNAPSHOT: ").append(solutionSpace.getPropertyForceSnapshot().getDirection()).append("\n"); } if (lattice.hasProperty(transformationJHPL, this.levelJHPL, solutionSpace.getPropertyInsufficientUtility())) { builder.append(" * INSUFFICIENT_UTILITY: ").append(solutionSpace.getPropertyInsufficientUtility().getDirection()).append("\n"); } if (lattice.hasProperty(transformationJHPL, this.levelJHPL, solutionSpace.getPropertySuccessorsPruned())) { builder.append(" * SUCCESSORS_PRUNED: ").append(solutionSpace.getPropertySuccessorsPruned().getDirection()).append("\n"); } if (lattice.hasProperty(transformationJHPL, this.levelJHPL, solutionSpace.getPropertyVisited())) { builder.append(" * VISITED: ").append(solutionSpace.getPropertyVisited().getDirection()).append("\n"); } builder.append("}"); return builder.toString(); } /** * Returns the sum of all transformation levels; * @param transformation * @return */ private int getLevel(int[] transformation) { int level = 0; for (int lvl : transformation) level += lvl; return level; } }