/* * This file is part of LibrePlan * * Copyright (C) 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.calendars.entities; import static org.libreplan.business.i18n.I18nHelper._; import java.util.Arrays; import java.util.Collection; import java.util.EnumMap; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.libreplan.business.workingday.EffortDuration; import org.libreplan.business.workingday.EffortDuration.Granularity; /** * This class is intended as a Hibernate component. * It's formed by two components, the standard effort and the allowed extra effort. * It represents the capacity for a resource. * * @author Óscar González Fernández <ogonzalez@igalia.com> */ public class Capacity { private EffortDuration standardEffort; private EffortDuration allowedExtraEffort; public static Capacity sum(Capacity... capacities) { return sum(Arrays.asList(capacities)); } public static Capacity sum(Collection<? extends Capacity> capacities) { EffortDuration standard = EffortDuration.zero(); EffortDuration extra = EffortDuration.zero(); for (Capacity each : capacities) { standard = standard.plus(each.getStandardEffort()); extra = extra == null || each.isOverAssignableWithoutLimit() ? null : extra.plus(each.getAllowedExtraEffort()); } return Capacity.create(standard).withAllowedExtraEffort(extra); } public static Capacity min(Capacity a, Capacity b) { return new Capacity(EffortDuration.min(a.getStandardEffort(), b.getStandardEffort()), minExtraEffort(a, b)); } private static EffortDuration minExtraEffort(Capacity a, Capacity b) { if (a.isOverAssignableWithoutLimit()) { return b.getAllowedExtraEffort(); } if (b.isOverAssignableWithoutLimit()) { return a.getAllowedExtraEffort(); } return EffortDuration.min(a.getAllowedExtraEffort(), b.getAllowedExtraEffort()); } public static Capacity max(Capacity a, Capacity b) { return new Capacity(EffortDuration.max(a.getStandardEffort(), b.getStandardEffort()), maxExtraEffort(a, b)); } private static EffortDuration maxExtraEffort(Capacity a, Capacity b) { if (a.isOverAssignableWithoutLimit() || b.isOverAssignableWithoutLimit()) { return null; } return EffortDuration.max(a.getAllowedExtraEffort(), b.getAllowedExtraEffort()); } public static Capacity create(EffortDuration standardEffort) { return new Capacity(standardEffort, null); } private static Capacity noCapacity() { return Capacity.create(EffortDuration.zero()).notOverAssignableWithoutLimit(); } public static Capacity zero() { return new Capacity(EffortDuration.zero(), EffortDuration.zero()); } /** * Default constructor for hibernate. DO NOT USE! * * @see Capacity#create(EffortDuration) */ public Capacity() { } private Capacity(EffortDuration standardEffort, EffortDuration extraHours) { Validate.notNull(standardEffort); this.standardEffort = standardEffort; this.allowedExtraEffort = extraHours; } public EffortDuration getStandardEffort() { if (standardEffort == null) { return EffortDuration.zero(); } return standardEffort; } public EffortDuration getAllowedExtraEffort() { return allowedExtraEffort; } public boolean isOverAssignableWithoutLimit() { return allowedExtraEffort == null; } public Capacity withAllowedExtraEffort(EffortDuration extraEffort) { return new Capacity(standardEffort, extraEffort); } public Capacity withStandardEffort(EffortDuration standardEffort) { return new Capacity(standardEffort, allowedExtraEffort); } public Capacity overAssignableWithoutLimit() { return overAssignableWithoutLimit(true); } public Capacity notOverAssignableWithoutLimit() { return overAssignableWithoutLimit(false); } public Capacity overAssignableWithoutLimit( boolean overAssignableWithoutLimit) { if (overAssignableWithoutLimit) { return new Capacity(standardEffort, null); } else { return new Capacity(standardEffort, allowedExtraEffort == null ? EffortDuration.zero() : allowedExtraEffort); } } public boolean isZero() { return standardEffort.isZero(); } public String getStandardEffortString() { return asString(getStandardEffort()); } public String getExtraEffortString() { if (getAllowedExtraEffort() == null) { return _("unlimited"); } return asString(getAllowedExtraEffort()); } private static String asString(EffortDuration duration) { if (duration == null) { return ""; } EnumMap<Granularity, Integer> values = duration.decompose(); Integer hours = values.get(Granularity.HOURS); Integer minutes = values.get(Granularity.MINUTES); return hours + ":" + minutes; } @Override public boolean equals(Object obj) { if (obj instanceof Capacity) { Capacity other = (Capacity) obj; return new EqualsBuilder() .append(standardEffort, other.standardEffort) .append(allowedExtraEffort, other.allowedExtraEffort) .isEquals(); } return false; } @Override public int hashCode() { return new HashCodeBuilder().append(standardEffort) .append(allowedExtraEffort).toHashCode(); } public EffortDuration limitDuration(EffortDuration duration) { if (isOverAssignableWithoutLimit()) { return duration; } return EffortDuration.min(standardEffort.plus(allowedExtraEffort), duration); } /** * <p> * Is the provided duration below the allowed duration? In that case there * is still spare space for more allocations. * </p> * <p> * The allowed duration is infinite if this {@link Capacity} is * {@link #overAssignableWithoutLimit(boolean)} or the duration provided is * less than the sum of the standard plus allowed extra effort. * </p> * * @param assignedDuration * @return */ public boolean hasSpareSpaceForMoreAllocations( EffortDuration assignedDuration) { Validate.notNull(assignedDuration); return isOverAssignableWithoutLimit() || assignedDuration.compareTo(standardEffort .plus(allowedExtraEffort)) < 0; } public Capacity minus(EffortDuration assignment) { if (!hasSpareSpaceForMoreAllocations(assignment)) { return noCapacity(); } EffortDuration newStandard = standardEffort.minus(EffortDuration.min( assignment, standardEffort)); EffortDuration pending = assignment.minus(EffortDuration.min( standardEffort, assignment)); EffortDuration newExtra = allowedExtraEffort == null ? null : allowedExtraEffort.minus(EffortDuration.min(pending, allowedExtraEffort)); return Capacity.create(newStandard).withAllowedExtraEffort(newExtra); } public boolean allowsWorking() { return !getStandardEffort().isZero() || isOverAssignableWithoutLimit() || !getAllowedExtraEffort().isZero(); } public Capacity multiplyBy(int capacity) { Validate.isTrue(capacity >= 0); return new Capacity(standardEffort.multiplyBy(capacity), allowedExtraEffort == null ? null : allowedExtraEffort.multiplyBy(capacity)); } @Override public String toString() { return "[" + getStandardEffortString() + " - " + getExtraEffortString() + "]"; } }