org.libreplan.business.planner.entities.allocationalgorithms.Distributor.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.business.planner.entities.allocationalgorithms.Distributor.java

Source

/*
 * This file is part of LibrePlan
 *
 * Copyright (C) 2009-2010 Fundacin para o Fomento da Calidade Industrial e
 *                         Desenvolvemento Tecnolxico de Galicia
 * Copyright (C) 2010-2011 Igalia, S.L.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.libreplan.business.planner.entities.allocationalgorithms;

import static org.libreplan.business.workingday.EffortDuration.seconds;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.commons.lang3.Validate;
import org.libreplan.business.calendars.entities.Capacity;
import org.libreplan.business.planner.entities.Share;
import org.libreplan.business.planner.entities.ShareDivision;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workingday.EffortDuration.IEffortFrom;

/**
 * Distributes an EffortDuration among several capacities. It respects the extra
 * hours requirements of the {@link Capacity capacities} and distributes the
 * effort evenly.
 *
 * @author scar Gonzlez Fernndez <ogonzalez@igalia.com>
 */
public class Distributor {

    public static Distributor among(Capacity... capacities) {
        return among(Arrays.asList(capacities));
    }

    public static Distributor among(Collection<? extends Capacity> capacities) {
        Validate.noNullElements(capacities);

        return new Distributor(capacities.toArray(new Capacity[0]));
    }

    private final Capacity[] capacities;

    private final List<Share> normalCapacityShares;

    private final List<Share> limitedOverloadShares;

    private final List<Share> unlimitedOverloadShares;

    private final List<List<Share>> phases = new ArrayList<List<Share>>();

    private Distributor(Capacity[] capacities) {
        Validate.notNull(capacities);
        this.capacities = capacities;
        this.normalCapacityShares = createNormalCapacityShares(capacities);
        this.limitedOverloadShares = createOverloadShares(capacities);
        this.unlimitedOverloadShares = createUnlimitedShares(capacities);
        this.phases.add(normalCapacityShares);
        this.phases.add(limitedOverloadShares);
        this.phases.add(this.unlimitedOverloadShares);
    }

    private static List<Share> createNormalCapacityShares(Capacity[] capacities) {
        List<Share> result = new ArrayList<Share>();
        for (Capacity each : capacities) {
            result.add(createNormalCapacityShare(each));
        }
        return result;
    }

    private List<Share> createOverloadShares(Capacity[] capacities) {
        List<Share> result = new ArrayList<Share>();
        EffortDuration maxExtraEffort = getMaxExtraEffort(capacities);
        for (Capacity each : capacities) {
            result.add(maxExtraEffort == null ? noSpaceAvailable() : createOverloadShare(each, maxExtraEffort));
        }
        return result;
    }

    private EffortDuration getMaxExtraEffort(Capacity[] capacities) {
        if (capacities.length == 0) {
            return null;
        }
        Capacity max = Collections.max(Arrays.asList(capacities), new Comparator<Capacity>() {

            @Override
            public int compare(Capacity o1, Capacity o2) {
                if (o1.getAllowedExtraEffort() == o2.getAllowedExtraEffort()) {
                    return 0;
                } else if (o1.getAllowedExtraEffort() == null) {
                    return -1;
                } else if (o2.getAllowedExtraEffort() == null) {
                    return 1;
                }
                return o1.getAllowedExtraEffort().compareTo(o2.getAllowedExtraEffort());
            }
        });
        return max.getAllowedExtraEffort();

    }

    private static Share createNormalCapacityShare(Capacity each) {
        return new Share(-each.getStandardEffort().getSeconds());
    }

    private Share createOverloadShare(Capacity each, EffortDuration maxExtraEffort) {
        if (each.getAllowedExtraEffort() == null && !each.isOverAssignableWithoutLimit()) {
            return noSpaceAvailable();
        }
        EffortDuration effort = each.getAllowedExtraEffort() != null ? each.getAllowedExtraEffort()
                : maxExtraEffort;
        return new Share(-effort.getSeconds());
    }

    private Share noSpaceAvailable() {
        return new Share(Integer.MAX_VALUE);
    }

    private List<Share> createUnlimitedShares(Capacity[] capacities) {
        List<Share> result = new ArrayList<Share>();
        for (Capacity each : capacities) {
            result.add(each.isOverAssignableWithoutLimit() ? new Share(0) : noSpaceAvailable());
        }
        return result;
    }

    public List<EffortDuration> distribute(EffortDuration effort) {
        EffortDuration[] result = new EffortDuration[capacities.length];
        Arrays.fill(result, EffortDuration.zero());

        for (List<Share> shares : phases) {
            EffortDuration remaining = effort.minus(sum(result));
            if (remaining.isZero()) {
                return asList(result);
            }
            result = limitByCapacities(sum(result, distribute(remaining, shares)));
        }
        return asList(result);
    }

    private EffortDuration[] sum(EffortDuration[] a, EffortDuration[] b) {
        EffortDuration[] result = new EffortDuration[a.length];
        for (int i = 0; i < result.length; i++) {
            result[i] = a[i].plus(b[i]);
        }
        return result;
    }

    private EffortDuration[] distribute(EffortDuration effort, List<Share> shares) {
        ShareDivision division = ShareDivision.create(shares);
        return fromSecondsToDurations(division.to(division.plus(effort.getSeconds())));
    }

    private List<EffortDuration> asList(EffortDuration[] acc) {
        return new ArrayList<EffortDuration>(Arrays.asList(acc));
    }

    private EffortDuration[] limitByCapacities(EffortDuration[] efforts) {
        EffortDuration[] result = new EffortDuration[efforts.length];
        for (int i = 0; i < efforts.length; i++) {
            result[i] = capacities[i].limitDuration(efforts[i]);
        }
        return result;
    }

    private EffortDuration[] fromSecondsToDurations(int[] seconds) {
        EffortDuration[] result = new EffortDuration[seconds.length];
        for (int i = 0; i < result.length; i++) {
            result[i] = seconds(seconds[i]);
        }
        return result;
    }

    private EffortDuration sum(EffortDuration[] durations) {
        return EffortDuration.sum(asList(durations), new IEffortFrom<EffortDuration>() {

            @Override
            public EffortDuration from(EffortDuration each) {
                return each;
            }
        });
    }

}