/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico 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; import static org.libreplan.business.workingday.EffortDuration.zero; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Objects; import org.apache.commons.lang3.Validate; import javax.validation.constraints.NotNull; import org.joda.time.LocalDate; import org.libreplan.business.common.BaseEntity; import org.libreplan.business.resources.entities.Resource; import org.libreplan.business.scenarios.entities.Scenario; import org.libreplan.business.util.deepcopy.AfterCopy; import org.libreplan.business.util.deepcopy.OnCopy; import org.libreplan.business.util.deepcopy.Strategy; import org.libreplan.business.workingday.EffortDuration; public abstract class DayAssignment extends BaseEntity { public enum FilterType { KEEP_ALL { @Override public boolean accepts(DayAssignment each) { return true; } }, WITHOUT_DERIVED { @Override public boolean accepts(DayAssignment each) { return !(each instanceof DerivedDayAssignment); } }; public abstract boolean accepts(DayAssignment each); } public static List<DayAssignment> filter(Collection<? extends DayAssignment> assignments, FilterType filter) { if (filter == null || filter.equals(FilterType.KEEP_ALL)) { return new ArrayList<>(assignments); } List<DayAssignment> result = new ArrayList<DayAssignment>(); for (DayAssignment each : assignments) { if ( filter.accepts(each) ) { result.add(each); } } return result; } public static <T extends DayAssignment> List<T> getAtInterval(List<T> orderedAssignments, LocalDate startInclusive, LocalDate endExclusive) { int position = findFirstAfterOrEqual(orderedAssignments, startInclusive); List<T> couldBeIncluded = orderedAssignments.subList(position, Math.max(orderedAssignments.size(), position)); List<T> result = new ArrayList<>(); for (T each : couldBeIncluded) { if ( each.getDay().compareTo(endExclusive) >= 0 ) { break; } assert each.includedIn(startInclusive, endExclusive); result.add(each); } return result; } public static <T extends DayAssignment> List<T> withConsolidatedValue( Collection<? extends T> assignments, boolean consolidated) { List<T> result = new ArrayList<>(); for (T each : assignments) { if ( each.isConsolidated() == consolidated ) { result.add(each); } } return result; } private static int findFirstAfterOrEqual( List<? extends DayAssignment> orderedAssignments, LocalDate startInclusive) { int start = 0; int end = orderedAssignments.size() - 1; while (start <= end) { int middle = start + (end - start) / 2; if ( orderedAssignments.get(middle).getDay().compareTo(startInclusive) < 0 ) { start = middle + 1; } else { end = middle - 1; } } return start; } public static EffortDuration sum(Collection<? extends DayAssignment> assignments) { EffortDuration result = zero(); for (DayAssignment each : assignments) { result = result.plus(each.getDuration()); } return result; } public static <T extends DayAssignment> Map<Resource, List<T>> byResourceAndOrdered( Collection<? extends T> assignments) { Map<Resource, List<T>> result = byResource(assignments); for (Entry<Resource, List<T>> entry : result.entrySet()) { Collections.sort(entry.getValue(), byDayComparator()); } return result; } public static <T extends DayAssignment> Map<Resource, List<T>> byResource(Collection<? extends T> assignments) { Map<Resource, List<T>> result = new HashMap<Resource, List<T>>(); for (T assignment : assignments) { Resource resource = assignment.getResource(); if (!result.containsKey(resource)) { result.put(resource, new ArrayList<T>()); } result.get(resource).add(assignment); } return result; } public static <T extends DayAssignment> Map<LocalDate, List<T>> byDay(Collection<? extends T> assignments) { Map<LocalDate, List<T>> result = new HashMap<LocalDate, List<T>>(); for (T t : assignments) { LocalDate day = t.getDay(); if ( !result.containsKey(day) ) { result.put(day, new ArrayList<T>()); } result.get(day).add(t); } return result; } public static Set<Resource> getAllResources(Collection<? extends DayAssignment> assignments) { Set<Resource> result = new HashSet<Resource>(); for (DayAssignment dayAssignment : assignments) { result.add(dayAssignment.getResource()); } return result; } public static <T extends DayAssignment> List<T> getOfType(Class<T> klass, Collection<? extends DayAssignment> dayAssignments) { List<T> result = new ArrayList<>(); for (DayAssignment each : dayAssignments) { if (klass.isInstance(each)) { result.add(klass.cast(each)); } } return result; } public static List<SpecificDayAssignment> specific( Collection<? extends DayAssignment> dayAssignments) { return getOfType(SpecificDayAssignment.class, dayAssignments); } public static List<GenericDayAssignment> generic( Collection<? extends DayAssignment> dayAssignments) { return getOfType(GenericDayAssignment.class, dayAssignments); } public static <T extends DayAssignment> List<T> withScenario( Scenario scenario, Collection<T> dayAssignments) { List<T> result = new ArrayList<T>(); for (T each : dayAssignments) { if (Objects.equals(each.getScenario(), scenario)) { result.add(each); } } return result; } @NotNull private EffortDuration duration; @NotNull private LocalDate day; @NotNull @OnCopy(Strategy.SHARE) private Resource resource; private Boolean consolidated = false; protected DayAssignment() { } protected DayAssignment(LocalDate day, EffortDuration duration, Resource resource) { Validate.notNull(day); Validate.notNull(duration); Validate.notNull(resource); this.day = day; this.duration = duration; this.resource = resource; } /* public int getHours() { return duration.getHours(); } */ public EffortDuration getDuration() { return duration; } public Resource getResource() { return resource; } public LocalDate getDay() { return day; } public void setConsolidated(Boolean consolidated) { this.consolidated = consolidated; } public boolean isConsolidated() { return consolidated == null ? false : consolidated; } public static Comparator<DayAssignment> byDayComparator() { return new Comparator<DayAssignment>() { @Override public int compare(DayAssignment assignment1, DayAssignment assignment2) { return assignment1.getDay().compareTo(assignment2.getDay()); } }; } public static Comparator<DayAssignment> byDurationComparator() { return new Comparator<DayAssignment>() { @Override public int compare(DayAssignment assignment1, DayAssignment assignment2) { return assignment1.getDuration().compareTo( assignment2.getDuration()); } }; } public static <T extends DayAssignment> List<T> orderedByDay( Collection<T> dayAssignments) { List<T> result = new ArrayList<>(dayAssignments); Collections.sort(result, byDayComparator()); return result; } public boolean isAssignedTo(Resource resource) { return this.resource.equals(resource); } public boolean includedIn(LocalDate startInclusive, LocalDate endExclusive) { return day.compareTo(startInclusive) >= 0 && day.compareTo(endExclusive) < 0; } @AfterCopy protected void associateToResource() { getResource().addNewAssignments(Arrays.asList(this)); } final void detach() { getResource().removeAssignments(Arrays.asList(this)); detachFromAllocation(); } protected abstract void detachFromAllocation(); public final boolean belongsToSomeOf(Map<Long, Set<BaseEntity>> allocations) { BaseEntity parent = getParent(); if (parent.getId() == null) { Set<BaseEntity> entitiesWithNullId = allocations.get(null); return entitiesWithNullId != null && entitiesWithNullId.contains(parent); } Set<BaseEntity> set = allocations.get(parent.getId()); return set != null; } protected abstract BaseEntity getParent(); public final boolean belongsTo(BaseEntity allocation) { if (allocation == null) { return false; } return belongsToSomeOf(BaseEntity.byId(Collections .singleton(allocation))); } /** * @return <code>null</code> if {@link DayAssignment this} day assignment * still has not been explicitly associated to a {@link Scenario} */ public abstract Scenario getScenario(); public abstract DayAssignment withDuration(EffortDuration newDuration); }