Package de.timefinder.algo

Source Code of de.timefinder.algo.ConflictMatrix

/*
* This file is part of the TimeFinder project.
*  Visit http://www.timefinder.de for more information.
*  Copyright (c) 2009 the original author or authors.
*
*  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 de.timefinder.algo;

import de.timefinder.algo.constraint.RasterConstraint;
import de.timefinder.data.Resource;
import de.timefinder.data.algo.Assignment;
import de.timefinder.data.set.DayRaster;
import de.timefinder.data.set.EventRaster;
import de.timefinder.data.set.EventRasterOneTreeMapImpl;
import de.timefinder.data.set.RasterCollection;
import javolution.util.FastMap;
import javolution.util.FastSet;

import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
* This class holds a matrix of Events to Events.
* The returned Events of getConflicting cannot be at the same timeslot as
* the specified one.
* E.g. resource R1 requires events A and B; resource R2 requires B and C; resource R2 requires A, B and D
* => getConflicting(A) = {B, D}
* => getConflicting(B) = {A, C, D}
* => getConflicting(C) = {B}
* <p/>
* HINT: the conflictMatrix won't be cloned if the Periode is cloned.
*
* @author Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net
*/
public class ConflictMatrix {

    private Map<Assignment, Set<Assignment>> conflictMatrix = new FastMap<Assignment, Set<Assignment>>();
    private Map<Assignment, RasterEntry> rasterMap = new FastMap<Assignment, RasterEntry>();
    private DayRaster dayRaster;
    private int slotsPerWeek;
    private boolean enabled = true;

    public ConflictMatrix(int slotsPerWeek, int slotsPerDay) {
        this.slotsPerWeek = slotsPerWeek;
        // use for all events the same dayRaster
        dayRaster = new DayRaster(slotsPerDay, slotsPerWeek);
    }

    public Set<Assignment> initFromResources(Map<? extends Resource, Set<Assignment>> assignments) {
        for (Entry<? extends Resource, Set<Assignment>> resEntry : assignments.entrySet()) {
            for (Assignment event : resEntry.getValue()) {
                Set<Assignment> tmpSet = getConflictingAssignments(event);
                for (Assignment e2 : resEntry.getValue()) {
                    // add event even if e == e2!
                    tmpSet.add(e2);
                }
            }
        }

        initAssignments(conflictMatrix.keySet());
        return conflictMatrix.keySet();
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    /**
     * Initialize raster for all events
     */
    public void initAssignments(Collection<Assignment> events) {
        for (Assignment ass : events) {
            RasterCollection combinedRaster = new RasterCollection();
            RasterConstraint rc = ass.getEvent().getConstraint(RasterConstraint.class);
            if (rc != null)
                combinedRaster.add(rc.getRaster().getForbidden());

            EventRaster eventRaster = new EventRasterOneTreeMapImpl(slotsPerWeek);
//            EventRaster eventRaster = new EventRasterTreeMapImpl(slotsPerWeek);
            combinedRaster.add(eventRaster);
            combinedRaster.add(dayRaster);
            rasterMap.put(ass, new RasterEntry(eventRaster, combinedRaster));
        }
    }

    /**
     * @return a collection of all events which must NOT be in the same
     *         timeslot as the specified Event ev.
     */
    public Set<Assignment> getConflictingAssignments(Assignment ev) {
        Set<Assignment> set = conflictMatrix.get(ev);
        if (set == null) {
            set = FastSet.newInstance();
            conflictMatrix.put(ev, set);
        }
        return set;
    }

    /**
     * @return the latest raster for the specified event. I.e. in the
     *         slots for which the raster is true the specified event cannot happen,
     *         because other conflicting events are already assigned there.
     */
    public RasterCollection getConflictingRaster(Assignment ass) {
        return getRasterEntry(ass).rasterCollection;
    }

    private RasterEntry getRasterEntry(Assignment event) {
        return rasterMap.get(event);
    }

    /**
     * Updates the start value in the EventRaster of all conflicting events
     * of the specified Event event
     */
    public void add(Assignment ass) {
        if (!enabled)
            return;

        if (ass.getStart() < 0)
            throw new IllegalArgumentException("Start cannot be negative! (" + ass + ")");

        RasterEntry entry = getRasterEntry(ass);
        if (entry.eventRaster.getRoot() != null)
            throw new IllegalStateException("rasters root event must be null while newly adding " + ass + " root:" + entry.eventRaster.getRoot());

        entry.eventRaster.setRoot(ass);

        // add conflicting events to the raster but only those which were already added to this matrix
        for (Assignment conflictingEvent : getConflictingAssignments(ass)) {
            if (conflictingEvent == ass)
                continue;

            RasterEntry cEntry = rasterMap.get(conflictingEvent);
            if (cEntry != null) { // event is already assigned to rasterMap

                if (!cEntry.eventRaster.add(ass))
                    throw new UnsupportedOperationException(ass + " should be added to raster at "
                            + " but wasn't. " + cEntry.eventRaster);
            }
        }
    }

    /**
     * This method removes the specified event with its start and duration
     * from all the conflicting events' rasters.
     */
    public void remove(Assignment ass) {
        if (!enabled)
            return;

        for (Assignment conflictingEvent : getConflictingAssignments(ass)) {
            if (conflictingEvent == ass)
                continue;

            RasterEntry tmpEntry = rasterMap.get(conflictingEvent);
            if (tmpEntry != null) {
                if (!tmpEntry.eventRaster.remove(ass))
                    throw new UnsupportedOperationException("Removing should be possible! "
                            + ass + " conflictingEvent:" + conflictingEvent + " raster:" + tmpEntry.eventRaster);
            }
        }

        // now it does NOT matter where conflicting events will be assigned
        RasterEntry e = rasterMap.get(ass);
        e.eventRaster.setRoot(null);
    }

    public Set<Assignment> calculateConflictingAssignments(Assignment currentEvent, int startTime) {
        return getRasterEntry(currentEvent).eventRaster.getConflictingAssignments(
                startTime, currentEvent.getEvent().getDuration());
    }

    private static class RasterEntry {

        private EventRaster eventRaster;
        private RasterCollection rasterCollection;

        public RasterEntry(EventRaster eventRaster, RasterCollection rasterCollection) {
            this.eventRaster = eventRaster;
            this.rasterCollection = rasterCollection;
        }

        @Override
        public String toString() {
            return rasterCollection.toString();
        }
//        public boolean isAssigned() {
//            return eventRaster.getRootEvent() != null;
//        }
    }
}
TOP

Related Classes of de.timefinder.algo.ConflictMatrix

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.