net.sourceforge.ganttproject.task.algorithm.SchedulerImpl.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.ganttproject.task.algorithm.SchedulerImpl.java

Source

/*
Copyright 2012 GanttProject Team
    
This file is part of GanttProject, an opensource project management tool.
    
GanttProject is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
GanttProject 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 Public License for more details.
    
You should have received a copy of the GNU General Public License
along with GanttProject.  If not, see <http://www.gnu.org/licenses/>.
*/
package net.sourceforge.ganttproject.task.algorithm;

import biz.ganttproject.core.calendar.GPCalendar;
import biz.ganttproject.core.calendar.GPCalendar.DayMask;
import biz.ganttproject.core.calendar.GPCalendarCalc;
import biz.ganttproject.core.time.CalendarFactory;
import biz.ganttproject.core.time.GanttCalendar;
import biz.ganttproject.core.time.TimeDuration;
import biz.ganttproject.core.time.TimeUnit;
import com.google.common.base.Supplier;
import com.google.common.collect.BoundType;
import com.google.common.collect.Lists;
import com.google.common.collect.Range;
import net.sourceforge.ganttproject.GPLogger;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade;
import net.sourceforge.ganttproject.task.TaskImpl;
import net.sourceforge.ganttproject.task.TaskMutator;
import net.sourceforge.ganttproject.task.algorithm.DependencyGraph.DependencyEdge;
import net.sourceforge.ganttproject.task.algorithm.DependencyGraph.ImplicitSubSuperTaskDependency;
import net.sourceforge.ganttproject.task.algorithm.DependencyGraph.Node;
import net.sourceforge.ganttproject.task.event.TaskDependencyEvent;
import net.sourceforge.ganttproject.task.event.TaskListener;
import net.sourceforge.ganttproject.task.event.TaskListenerAdapter;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

/**
 * This class walk the dependency graph and updates start and end dates of tasks
 * according to information returned by dependency edges.
 *
 * @author dbarashev
 */
public class SchedulerImpl extends AlgorithmBase {
    private final DependencyGraph myGraph;
    private boolean isRunning;
    private final Supplier<TaskContainmentHierarchyFacade> myTaskHierarchy;
    private final TaskListener myTaskListener;

    public SchedulerImpl(DependencyGraph graph, Supplier<TaskContainmentHierarchyFacade> taskHierarchy) {
        myGraph = graph;
        myGraph.addListener(new DependencyGraph.Listener() {
            @Override
            public void onChange() {
                run();
            }
        });
        myTaskHierarchy = taskHierarchy;
        myTaskListener = new TaskListenerAdapter() {
            @Override
            public void dependencyChanged(TaskDependencyEvent e) {
                run();
            }
        };
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        if (isEnabled()) {
            run();
        }
    }

    public TaskListener getTaskModelListener() {
        return myTaskListener;
    }

    @Override
    public void run() {
        if (!isEnabled() || isRunning) {
            return;
        }
        isRunning = true;
        try {
            doRun();
        } finally {
            isRunning = false;
        }
    }

    private void doRun() {
        int layers = myGraph.checkLayerValidity();
        for (int i = 0; i < layers; i++) {
            Collection<Node> layer = myGraph.getLayer(i);
            for (Node node : layer) {
                try {
                    schedule(node);
                } catch (IllegalArgumentException e) {
                    GPLogger.log(e);
                }
            }
        }
    }

    private void schedule(Node node) {
        Logger logger = GPLogger.getLogger(this);
        GPLogger.debug(logger, "Scheduling node %s", node);
        Range<Date> startRange = Range.all();
        Range<Date> endRange = Range.all();

        Range<Date> weakStartRange = Range.all();
        Range<Date> weakEndRange = Range.all();

        List<Date> subtaskRanges = Lists.newArrayList();
        List<DependencyEdge> incoming = node.getIncoming();
        GPLogger.debug(logger, ".. #incoming edges=%d", incoming.size());
        for (DependencyEdge edge : incoming) {
            if (!edge.refresh()) {
                continue;
            }
            if (edge instanceof ImplicitSubSuperTaskDependency) {
                subtaskRanges.add(edge.getStartRange().upperEndpoint());
                subtaskRanges.add(edge.getEndRange().lowerEndpoint());
            } else {
                if (edge.isWeak()) {
                    weakStartRange = weakStartRange.intersection(edge.getStartRange());
                    weakEndRange = weakEndRange.intersection(edge.getEndRange());
                } else {
                    startRange = startRange.intersection(edge.getStartRange());
                    endRange = endRange.intersection(edge.getEndRange());
                }
            }
            if (startRange.isEmpty() || endRange.isEmpty()) {
                GPLogger.logToLogger("both start and end ranges were calculated as empty for task=" + node.getTask()
                        + ". Skipping it");
            }
        }
        GPLogger.debug(logger, "..Ranges: start=%s end=%s weakStart=%s weakEnd=%s", startRange, endRange,
                weakStartRange, weakEndRange);

        Range<Date> subtasksSpan = subtaskRanges.isEmpty()
                ? Range.closed(node.getTask().getStart().getTime(), node.getTask().getEnd().getTime())
                : Range.encloseAll(subtaskRanges);
        Range<Date> subtreeStartUpwards = subtasksSpan
                .span(Range.downTo(node.getTask().getStart().getTime(), BoundType.CLOSED));
        Range<Date> subtreeEndDownwards = subtasksSpan
                .span(Range.upTo(node.getTask().getEnd().getTime(), BoundType.CLOSED));
        GPLogger.debug(logger, "..Subtasks span=%s", subtasksSpan);

        if (!startRange.equals(Range.all())) {
            startRange = startRange.intersection(weakStartRange);
        } else if (!weakStartRange.equals(Range.all())) {
            startRange = weakStartRange.intersection(subtreeStartUpwards);
        }
        if (!endRange.equals(Range.all())) {
            endRange = endRange.intersection(weakEndRange);
        } else if (!weakEndRange.equals(Range.all())) {
            endRange = weakEndRange.intersection(subtreeEndDownwards);
        }
        if (node.getTask().getThirdDateConstraint() == TaskImpl.EARLIESTBEGIN
                && node.getTask().getThird() != null) {
            startRange = startRange
                    .intersection(Range.downTo(node.getTask().getThird().getTime(), BoundType.CLOSED));
            GPLogger.debug(logger, ".. applying earliest start=%s. Now start range=%s", node.getTask().getThird(),
                    startRange);
        }
        if (!subtaskRanges.isEmpty()) {
            startRange = startRange.intersection(subtasksSpan);
            endRange = endRange.intersection(subtasksSpan);
        }
        GPLogger.debug(logger, ".. finally, start range=%s", startRange);
        if (startRange.hasLowerBound()) {
            modifyTaskStart(node.getTask(), startRange.lowerEndpoint());
        }
        if (endRange.hasUpperBound()) {
            GPCalendarCalc cal = node.getTask().getManager().getCalendar();
            Date endDate = endRange.upperEndpoint();
            TimeUnit timeUnit = node.getTask().getDuration().getTimeUnit();
            if (DayMask.WORKING == (cal.getDayMask(endDate) & DayMask.WORKING)) {
                // in case if calculated end date falls on first day after holidays (say, on Monday)
                // we'll want to modify it a little bit, so that it falls on that holidays start
                // If we don't do this, it will be done automatically the next time task activities are recalculated,
                // and thus task end date will keep changing
                Date closestWorkingEndDate = cal.findClosest(endDate, timeUnit,
                        GPCalendarCalc.MoveDirection.BACKWARD, GPCalendar.DayType.WORKING);
                Date closestNonWorkingEndDate = cal.findClosest(endDate, timeUnit,
                        GPCalendarCalc.MoveDirection.BACKWARD, GPCalendar.DayType.NON_WORKING,
                        closestWorkingEndDate);
                // If there is a non-working date between current task end and closest working date
                // then we're really just after holidays
                if (closestNonWorkingEndDate != null && closestWorkingEndDate.before(closestNonWorkingEndDate)) {
                    // we need to adjust-right closest working date to position to the very beginning of the holidays interval
                    Date nonWorkingPeriodStart = timeUnit.adjustRight(closestWorkingEndDate);
                    if (nonWorkingPeriodStart.after(node.getTask().getStart().getTime())) {
                        endDate = nonWorkingPeriodStart;
                    }
                }
            }
            modifyTaskEnd(node.getTask(), endDate);
        }
    }

    private void modifyTaskEnd(Task task, Date newEnd) {
        if (task.getEnd().getTime().equals(newEnd)) {
            return;
        }
        GanttCalendar newEndCalendar = CalendarFactory.createGanttCalendar(newEnd);
        if (getDiagnostic() != null) {
            getDiagnostic().addModifiedTask(task, null, newEnd);
        }
        TaskMutator mutator = task.createMutator();
        mutator.setEnd(newEndCalendar);
        mutator.commit();
    }

    private void modifyTaskStart(Task task, Date newStart) {
        if (task.getStart().getTime().equals(newStart)) {
            return;
        }
        GanttCalendar newStartCalendar = CalendarFactory.createGanttCalendar(newStart);
        if (getDiagnostic() != null) {
            getDiagnostic().addModifiedTask(task, newStart, null);
        }
        TaskMutator mutator = task.createMutator();
        if (myTaskHierarchy.get().hasNestedTasks(task)) {
            mutator.setStart(newStartCalendar);
            mutator.commit();
        } else {
            TimeDuration shift = task.getManager().createLength(task.getDuration().getTimeUnit(),
                    task.getStart().getTime(), newStart);
            mutator.shift(shift);
            mutator.commit();
        }
    }
}