org.mqnaas.core.impl.slicing.SliceAdministration.java Source code

Java tutorial

Introduction

Here is the source code for org.mqnaas.core.impl.slicing.SliceAdministration.java

Source

package org.mqnaas.core.impl.slicing;

/*
 * #%L
 * MQNaaS :: Core
 * %%
 * Copyright (C) 2007 - 2015 Fundaci Privada i2CAT, Internet i Innovaci a Catalunya
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 * #L%
 */

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.SerializationUtils;
import org.mqnaas.core.api.IResource;
import org.mqnaas.core.api.IServiceProvider;
import org.mqnaas.core.api.annotations.DependingOn;
import org.mqnaas.core.api.annotations.Resource;
import org.mqnaas.core.api.exceptions.ApplicationActivationException;
import org.mqnaas.core.api.exceptions.CapabilityNotFoundException;
import org.mqnaas.core.api.slicing.Cube;
import org.mqnaas.core.api.slicing.CubesList;
import org.mqnaas.core.api.slicing.ISliceAdministration;
import org.mqnaas.core.api.slicing.Range;
import org.mqnaas.core.api.slicing.SlicingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author Georg Mansky-Kummert (i2CAT)
 * @author Adrin Rosell Rey (i2CAT)
 *
 */
public class SliceAdministration implements ISliceAdministration {

    @DependingOn
    private IServiceProvider serviceProvider;

    @Resource
    private IResource resource;

    private Slice slice;

    private static final Logger log = LoggerFactory.getLogger(SliceAdministration.class);

    Object originalData; // original cube
    Object currentData; // current cube

    // Once the currentData is initialized, this variable contains the number of dimensions
    private int nDimensions;

    public static boolean isSupporting(IResource resource) {
        return (resource instanceof SliceResource);
    }

    @Override
    public void activate() throws ApplicationActivationException {
        log.info("Initializing SliceAdministration capability for resource " + resource.getId());
        slice = new Slice(resource, serviceProvider);
        log.info("Initialized SliceAdministration capability for resource " + resource.getId());
    }

    @Override
    public void deactivate() {
        log.info("Deactivating SliceAdministration capability of resource " + resource.getId());
        originalData = null;
        currentData = null;
        slice = null;
        nDimensions = 0;
        log.info("Deactivated SliceAdministration capability of resource " + resource.getId());

    }

    /**
     * Initializes the given {@link Cube} in the space of this slice, e.g. defines the elements within the <code>cube</code> as slicing units.
     *
     * IMPORTANT: The original slice information will contain the values of the set of {@link Cube} passed as arguments the first time this method is
     * called!
     */
    @Override
    public void setCubes(CubesList cubes) {

        if (cubes == null)
            throw new IllegalArgumentException("The list of cubes of the slice can not be null.");

        log.info("Setting cubes in slice " + slice.getResource().getId());

        try {
            initData();

            List<Unit> units = slice.getUnits();

            int[] lowerBounds = new int[units.size()];
            int[] upperBounds = new int[units.size()];

            for (Cube cube : cubes.getCubes()) {

                log.debug("Found cube to be set in slice " + slice.getResource().getId());

                Range[] ranges = cube.getRanges();
                int i = 0;

                for (Unit unit : units) {
                    int lowerBound = unit.getRange().getLowerBound();

                    lowerBounds[i] = ranges[i].getLowerBound() - lowerBound;
                    upperBounds[i] = ranges[i].getUpperBound() - lowerBound;
                    log.trace("Unit: " + unit.getName() + "\t LowerBound: " + lowerBounds[i] + "\t UpperBound: "
                            + upperBounds[i]);

                    i++;

                }

                SetOperation set = new SetOperation();
                executeOperation(null, lowerBounds, upperBounds, set);

                log.debug("Cube unset from slice " + slice.getResource().getId());

            }

            // if is the first time this method is call, we must initialize the originalData values as a copy of the currentData one.
            if (originalData == null)
                originalData = cloneSliceData(currentData, units.size());

            log.info("Cubes set in slice " + slice.getResource().getId());

        } catch (CapabilityNotFoundException e) {
            throw new RuntimeException(
                    "Given slice " + slice.getResource().getId() + " does not support necessary capability", e);
        }

    }

    @Override
    public boolean contains(IResource otherResource) throws SlicingException {
        try {
            initData();

            Slice other = new Slice(otherResource, serviceProvider);

            compareSliceDefinition(other);

            List<Unit> units = slice.getUnits();

            int[] lbs = new int[units.size()], ubs = new int[units.size()];
            initUpperBounds(ubs);

            ContainsOperation contains = new ContainsOperation();
            executeOperation(other.getData(), lbs, ubs, contains);
            return contains.getResult();
        } catch (CapabilityNotFoundException e) {
            throw new RuntimeException("Given slice " + slice + " does not support necessary capability", e);
        }

    }

    @Override
    public void cut(IResource otherResource) throws SlicingException {

        if (otherResource == null)
            throw new NullPointerException("Can't cut a null slice from slice " + slice.getResource().getId());
        if (!(otherResource instanceof SliceResource))
            throw new IllegalArgumentException("Only SliceResources cant be cut from other slices.");

        try {
            log.info("Cutting slice " + otherResource.getId() + " from slice " + slice.getResource().getId());
            initData();

            Slice other = new Slice(otherResource, serviceProvider);

            compareSliceDefinition(other);

            List<Unit> units = slice.getUnits();

            int[] lbs = new int[units.size()], ubs = new int[units.size()];
            initUpperBounds(ubs);

            ContainsOperation contains = new ContainsOperation();
            executeOperation(other.getData(), lbs, ubs, contains);

            if (!contains.getResult())
                throw new SlicingException("Given slice contains values that are not in the original slice.");

            CutOperation cut = new CutOperation();
            executeOperation(other.getData(), lbs, ubs, cut);

            log.info("Slice + " + otherResource.getId() + " cut from " + slice.getResource().getId());
        } catch (CapabilityNotFoundException e) {
            throw new RuntimeException("Given slice " + slice + " does not support necessary capability", e);
        }
    }

    @Override
    public void add(IResource otherResource) throws SlicingException {

        if (otherResource == null)
            throw new NullPointerException("Can't add a null slice to slice " + slice.getResource().getId());
        if (!(otherResource instanceof SliceResource))
            throw new IllegalArgumentException("Only SliceResources can be added to other slices.");

        try {
            log.info("Adding slice + " + otherResource.getId() + " to slice " + slice.getResource().getId());
            initData();

            Slice other = new Slice(otherResource, serviceProvider);

            compareSliceDefinition(other);

            if (other.isInOperationalState())
                throw new SlicingException(
                        "Can not add slice to current one since given slice is in operational state.");

            List<Unit> units = slice.getUnits();

            int[] lbs = new int[units.size()], ubs = new int[units.size()];
            initUpperBounds(ubs);

            NotContainsOperation preAdd = new NotContainsOperation();
            executeOperation(other.getData(), lbs, ubs, preAdd);

            if (!preAdd.getResult())
                throw new SlicingException("Given slice contains values that are already in the original slice.");

            AddOperation add = new AddOperation();
            executeOperation(other.getData(), lbs, ubs, add);

            log.info("Slice " + otherResource.getId() + " added to slice " + slice.getResource().getId());

        } catch (CapabilityNotFoundException e) {
            throw new RuntimeException("Given slice " + slice + " does not support necessary capability", e);
        }

    }

    @Override
    public CubesList getCubes() {
        return originalData != null ? new CubesList(compactize(originalData)) : null;
    }

    @Override
    public CubesList getAvailableCubes() {
        return currentData != null ? new CubesList(compactize(currentData)) : null;

    }

    /**
     * Gets the current value of the slice unit specified by the indexes stored in the <code>coords</code> array.
     * 
     * @param coords
     *            Position of the slice which value we want to retrieve.
     * @return The current state of the slice unit. <code>true</code> if the slice unit is part of the slice and it's available. <code>false</code>
     *         otherwise.
     * 
     */
    // boolean get(Object data, int[] coords) {
    // return get(data, coords);
    // }

    /**
     * A {@link SliceResource} is in operational state if it's current space and the original one does not match. That means, if the slice has been
     * divided in sub-slices.
     * 
     * @return <code>true</code> If any of the initial cube of the slice space has been assigned to another sub-slice. <code>false</code> otherwise.
     */
    public boolean isInOperationalState() {

        log.info("Cheking if slice " + slice.getResource().getId() + " is in operational state.");

        List<Unit> units = slice.getUnits();

        int[] lbs = new int[units.size()], ubs = new int[units.size()];
        initUpperBounds(ubs);

        int sizes[] = new int[ubs.length];
        for (int i = 0; i < ubs.length; i++)
            sizes[i] = ubs[i] + 1;

        ContainsOperation contains = new ContainsOperation();
        executeOperation(originalData, lbs, ubs, contains);

        return !contains.getResult();
    }

    /**
     * Marks the set of cubes as unavailable in the slice space. Operation will be performed in the current space structure.
     * 
     * @param cubes
     *            Cubes that will be markes as unavaiable in the current live space.
     */
    @Override
    public void unsetCubes(CubesList cubes) {

        if (cubes == null || cubes.getCubes() == null)
            throw new NullPointerException("List of cubes can not be null.");

        log.info("Unsetting cubes from slice " + slice.getResource().getId());

        List<Unit> units = slice.getUnits();

        int[] lowerBounds = new int[units.size()];
        int[] upperBounds = new int[units.size()];

        for (Cube cube : cubes.getCubes()) {

            log.debug("Found cube to be unset from slice " + slice.getResource().getId());

            Range[] ranges = cube.getRanges();
            for (int i = 0; i < units.size(); i++) {
                int lowerBound = units.get(i).getRange().getLowerBound();

                lowerBounds[i] = ranges[i].getLowerBound() - lowerBound;
                upperBounds[i] = ranges[i].getUpperBound() - lowerBound;

                log.trace("Unit: " + units.get(i).getName() + "\t LowerBound: " + lowerBounds[i] + "\t UpperBound: "
                        + upperBounds[i]);

            }

            UnsetOperation set = new UnsetOperation();
            executeOperation(null, lowerBounds, upperBounds, set);

            log.debug("Cube unset from slice " + slice.getResource().getId());
        }

        log.info("Cubes unset from slice " + slice.getResource().getId());
    }

    /**
     * Initialize the internal structures of the this capability, i.e., the information of the current slice information.
     */
    private void initData() throws CapabilityNotFoundException {
        if (currentData == null) {
            List<Unit> units = slice.getUnits();

            nDimensions = units.size();

            int[] dimensions = new int[units.size()];
            int i = 0;
            for (Unit unit : units)
                dimensions[i++] = unit.getRange().size();

            currentData = Array.newInstance(boolean.class, dimensions);

        }
    }

    /**
     * Clones the <code>source</code> slice space. Up to 3D implemented.
     */
    private Object cloneSliceData(Object source, int dimensions) {
        switch (dimensions) {
        case 1:
            return SerializationUtils.clone((boolean[]) source);
        case 2:
            return SerializationUtils.clone((boolean[][]) source);
        case 3:
            return SerializationUtils.clone((boolean[][][]) source);
        default:
            throw new RuntimeException("Only up to three dimensions implemented");
        }
    }

    /**
     * Sets the <code>value</code> boolean value into the <code>currentData</code> array position defined by the <code>coords</code> array. Up to
     * three dimensions implemented.
     * 
     * @param data
     * 
     * @param coords
     *            Position of the <code>data</code> array defined by an array of indexes.
     * @param value
     *            boolean value to be set in this position.
     */
    private void set(Object data, int[] coords, boolean value) {
        switch (nDimensions) {
        case 1:
            boolean d1[] = (boolean[]) data;
            d1[coords[0]] = value;
            break;
        case 2:
            boolean d2[][] = (boolean[][]) data;
            d2[coords[0]][coords[1]] = value;
            break;
        case 3:
            boolean d3[][][] = (boolean[][][]) data;
            d3[coords[0]][coords[1]][coords[2]] = value;
            break;
        default:
            throw new RuntimeException("Only up to three dimensions implemented");
        }
    }

    /**
     * Sets the <code>value</code> boolean value into the <code>currentData</code> array position defined by the <code>coords</code> array. Up to
     * three dimensions implemented.
     * 
     * @param coords
     *            Position of the <code>data</code> array defined by an array of indexes.
     * @param value
     *            boolean value to be set in this position.
     */
    private void set(int[] coords, boolean value) {
        set(currentData, coords, value);
    }

    /**
     * Gets the value of the slice unit specified by the indexes stored in the <code>coords</code> array in the <code>data</code> slice information,
     * which could be either the original slice information or the current one.
     * 
     * @param coords
     *            Position of the slice which value we want to retrieve.
     * @return The state of the slice unit in the <code>data</code> slice unit information. <code>true</code> if the slice unit is part of the slice
     *         and it's available. <code>false</code> otherwise.
     * 
     */
    private boolean get(Object data, int[] coords) {
        switch (nDimensions) {
        case 1:
            boolean d1[] = (boolean[]) data;
            return d1[coords[0]];
        case 2:
            boolean d2[][] = (boolean[][]) data;
            return d2[coords[0]][coords[1]];
        case 3:
            boolean d3[][][] = (boolean[][][]) data;
            return d3[coords[0]][coords[1]][coords[2]];
        default:
            throw new RuntimeException("Only up to three dimensions implemented");
        }
    }

    boolean get(int[] coords) {
        return get(currentData, coords);
    }

    /**
     * Specifies and sets the upper bounds of each dimension of the slice information. It's generic for all number of dimendsions.
     * 
     * @param ubs
     *            Array where the upper bounds will be stored.
     */
    private void initUpperBounds(int[] ubs) {
        Object it = currentData;

        for (int i = 0; i < ubs.length; i++) {
            ubs[i] = Array.getLength(it) - 1;
            it = Array.get(it, 0);
        }
    }

    private List<Cube> compactize(Object data) {

        List<Unit> units = slice.getUnits();

        int[] lbs = new int[units.size()], ubs = new int[units.size()];
        initUpperBounds(ubs);

        Object dataCopy = cloneSliceData(data, units.size());
        CompatizeOperation operation = new CompatizeOperation();
        executeOperation(dataCopy, lbs, ubs, operation);

        List<Cube> cubes = operation.getCubes();

        // Now adapt the lower bounds...
        for (Cube cube : cubes) {
            int i = 0;
            Range[] ranges = cube.getRanges();

            for (Unit unit : units) {
                int unitLowerBound = unit.getRange().getLowerBound();
                ranges[i].setLowerBound(ranges[i].getLowerBound() + unitLowerBound);
                ranges[i].setUpperBound(ranges[i].getUpperBound() + unitLowerBound);
                i++;
            }

            cube.setRanges(ranges);
        }

        return cubes;
    }

    private interface Operation {

        boolean execute(Object dataOfOtherSlice, int[] coords);
    }

    /**
     * Specifies whether a specific position of the slice managed by this capability instance was part of the original slice information and is still
     * available, if the slice managed by the other capability requires this position. The position is indicated by an array of integers, which length
     * is the number of dimensions of the slice and the coords[i] contains the index of the i-axis.
     */
    private class ContainsOperation implements Operation {

        private boolean result = true;

        @Override
        public boolean execute(Object dataOfOtherSlice, int[] coords) {
            if (get(dataOfOtherSlice, coords)) {
                // the other slice needs this element
                result &= (get(currentData, coords) && get(originalData, coords));
                return result;
            }

            // the other slice does not need this element, its value is not important
            return true;
        }

        public boolean getResult() {
            return result;
        }

    }

    /**
     * Specifies whether a specific position of the slice managed by this capability instance is not available in the current slice information, but
     * was part of the original slice information, only if the slice managed by the other capability contanis this position. The position is indicated
     * by an array of integers, which length is the number of dimensions of the slice and the coords[i] contains the index of the i-axis.
     */
    private class NotContainsOperation implements Operation {

        private boolean result = true;

        @Override
        public boolean execute(Object dataOfOtherSlice, int[] coords) {
            if (get(dataOfOtherSlice, coords) && get(currentData, coords))
                result = false;

            return result;

        }

        public boolean getResult() {
            return result;
        }

    }

    /**
     * Sets as available a specific position of the current slice information managed by this capability. The position is indicated by an array of
     * integers, which length is the number of dimensions of the slice and the coords[i] contains the index of the i-axis.
     */
    private class SetOperation implements Operation {

        @Override
        public boolean execute(Object dataOfOtherSlice, int[] coords) {
            set(coords, true);

            return true;
        }

    }

    /**
     * Sets as unavailable a specific position of the current slice information managed by this capability. The position is indicated by an array of
     * integers, which length is the number of dimensions of the slice and the coords[i] contains the index of the i-axis.
     */
    private class UnsetOperation implements Operation {

        @Override
        public boolean execute(Object dataOfOtherSlice, int[] coords) {
            set(coords, false);

            return true;
        }

    }

    /**
     * If the slice information position of the slice managed by the other sliceAdminsitration capability was part of it, and it's not available in
     * the current slice but was part of the slice managed by this capability instance, it sets the specific slice unit as avaiable in the current
     * slice. The position is indicated by an array of integers, which length is the number of dimensions of the slice and the coords[i] contains the
     * index of the i-axis.
     */
    private class AddOperation implements Operation {

        @Override
        public boolean execute(Object dataOfOtherSlice, int[] coords) {
            // we can only add elements that were part of the original slice.
            if (get(dataOfOtherSlice, coords) && get(originalData, coords))
                set(coords, true);

            return true;

        }

    }

    /**
     * If the slice managed by the other sliceAdministration capability requires a specific position of the current slice manages by this capability
     * instance, it sets this position as unavailable in the mentioned position of the current slice information. The position is indicated by an
     * array of integers, which length is the number of dimensions of the slice and the coords[i] contains the index of the i-axis.
     *
     */
    private class CutOperation implements Operation {

        @Override
        public boolean execute(Object dataOfOtherSlice, int[] coords) {
            if (get(dataOfOtherSlice, coords))
                set(coords, false);
            return true;
        }

    }

    /**
     * Builds a list of {@link Cube} from the slice space.
     */
    private class CompatizeOperation implements Operation {

        private List<Cube> cubes = new ArrayList<Cube>();

        @Override
        public boolean execute(Object dataOfOtherSlice, int[] coords) {
            if (get(dataOfOtherSlice, coords)) {

                // Start the fill along all axis from that point
                int n = coords.length;
                int[] dimensions = new int[n];
                initUpperBounds(dimensions);

                // Initialize the cube: start with a single element
                Range[] ranges = new Range[n];
                for (int i = 0; i < n; i++) {
                    ranges[i] = new Range(coords[i], coords[i]);
                }

                IsPartOfSliceOperation partOfSlice = new IsPartOfSliceOperation();

                int[] lbs = new int[n], ubs = new int[n];

                // Now search as far as possible along each axis
                for (int searchAxis = 0; searchAxis < n; searchAxis++) {

                    // Is a search possible?
                    if (ranges[searchAxis].getUpperBound() >= dimensions[searchAxis]) {
                        continue;
                    }

                    partOfSlice.setResult(true);

                    do {
                        // Initialize the search cube
                        for (int i = 0; i < n; i++) {
                            lbs[i] = i == searchAxis ? ranges[i].getUpperBound() + 1 : ranges[i].getLowerBound();
                            ubs[i] = i == searchAxis ? ranges[i].getUpperBound() + 1 : ranges[i].getUpperBound();
                        }

                        // Search cube contained?
                        executeOperation(dataOfOtherSlice, lbs, ubs, partOfSlice);

                        if (partOfSlice.getResult()) {
                            // Yes, enlarge our cube on that axis
                            ranges[searchAxis].setUpperBound(ranges[searchAxis].getUpperBound() + 1);
                        }

                    } while (partOfSlice.getResult()
                            && ranges[searchAxis].getUpperBound() < dimensions[searchAxis]);
                }

                // Mark the cube as visited and add to our result list
                for (int i = 0; i < n; i++) {
                    lbs[i] = ranges[i].getLowerBound();
                    ubs[i] = ranges[i].getUpperBound();
                }

                executeOperation(dataOfOtherSlice, lbs, ubs, new ClearOperation());

                Cube cube = new Cube(ranges);
                cubes.add(cube);
            }
            return true;
        }

        public List<Cube> getCubes() {
            return cubes;
        }

    }

    private class ClearOperation implements Operation {

        @Override
        public boolean execute(Object dataOfOtherSlice, int[] coords) {
            set(dataOfOtherSlice, coords, false);
            return true;
        }

    }

    /**
     * Checks if a specific coordinate is part of a slice and if it's available.
     */
    private class IsPartOfSliceOperation implements Operation {

        boolean result = true;

        @Override
        public boolean execute(Object dataOfOtherSlice, int[] coords) {

            this.result = get(dataOfOtherSlice, coords);

            return result;
        }

        public boolean getResult() {
            return result;
        }

        public void setResult(boolean result) {
            this.result = result;
        }

    }

    /**
     * Executes a specific operation involving the {@link SliceResource} managed by this capability instance and the slice managed by the
     * <code>other</code> {@link SliceAdministration} capability. This method is generic for n-dimensions slices, defined by the lenght of the arrays
     * <code>lbs</code> and <code>ubs</code>
     * 
     * @param other
     *            Capability managing the other slice involves in the operation.
     * @param lbs
     *            Array of length "n", where "n" defines the dimensions of the slice. Value lbs[i] contains the lower bound index of the i-axis.
     * @param ubs
     *            Array of length "n", where "n" defines the dimensions of the slice. Value ubs[i] contains the upper bound index of the i-axis.
     * @param operation
     *            Operation to be executed.
     */
    private void executeOperation(Object dataOfOtherSlice, int[] lbs, int[] ubs, Operation operation) {
        int l = lbs.length;

        int[] c = Arrays.copyOfRange(lbs, 0, l);

        do {

            if (!operation.execute(dataOfOtherSlice, c))
                return;

            int i = 0;
            c[i]++;
            while (i < l - 1 && c[i] > ubs[i]) {
                c[i] = lbs[i];
                i++;
                c[i]++;
            }
            // while the value is in range lbs <= x < ubs
        } while (c[l - 1] <= ubs[l - 1]);
    }

    private void compareSliceDefinition(Slice other) throws CapabilityNotFoundException {

        log.debug("Comparing if both slices have same dimensions and same length for each slice units.");

        List<Unit> units = slice.getUnits();
        List<Unit> otherUnits = other.getUnits();

        log.trace(slice.getResource().getId() + ": " + units.size() + "dimensions, " + other.getResource().getId()
                + ": " + otherUnits.size() + " dimensions");

        if (otherUnits.size() != units.size())
            throw new IllegalArgumentException("Only slices with same dimensions can be compared.");

        for (int i = 0; i < units.size(); i++)
            if (!units.get(i).equals(otherUnits.get(i)))
                throw new IllegalArgumentException("Slices units must be defined in same order.");

        switch (units.size()) {
        case 1:
            boolean d1[] = (boolean[]) currentData;
            boolean otherD1[] = (boolean[]) other.getData();

            if (d1.length != otherD1.length)
                throw new IllegalArgumentException("Slices have different size for slice unit " + units.get(0));

            break;
        case 2:

            boolean d2[][] = (boolean[][]) currentData;
            boolean otherD2[][] = (boolean[][]) other.getData();

            if (d2.length != otherD2.length)
                throw new IllegalArgumentException("Slices have different size for slice unit " + units.get(0));
            if (d2[0].length != otherD2[0].length)
                throw new IllegalArgumentException("Slices have different size for slice unit " + units.get(1));

            break;
        case 3:
            boolean d3[][][] = (boolean[][][]) currentData;
            boolean otherD3[][][] = (boolean[][][]) other.getData();

            if (d3.length != otherD3.length)
                throw new IllegalArgumentException("Slices have different size for slice unit " + units.get(0));
            if (d3[0].length != otherD3[0].length)
                throw new IllegalArgumentException("Slices have different size for slice unit " + units.get(1));
            if (d3[0][0].length != otherD3[0][0].length)
                throw new IllegalArgumentException("Slices have different size for slice unit " + units.get(2));

            break;
        default:
            throw new RuntimeException("Only up to three dimensions implemented");
        }

        log.debug("Slices comparison ended successfully.");

    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((currentData == null) ? 0 : currentData.hashCode());
        result = prime * result + ((originalData == null) ? 0 : originalData.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        SliceAdministration other = (SliceAdministration) obj;
        if (currentData == null) {
            if (other.currentData != null)
                return false;
        } else if (!currentData.equals(other.currentData))
            return false;
        if (originalData == null) {
            if (other.originalData != null)
                return false;
        } else if (!originalData.equals(other.originalData))
            return false;
        return true;
    }

    @Override
    public Object getData() {
        return currentData;
    }

}