/*
* 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.orders.entities;
import static org.libreplan.business.i18n.I18nHelper._;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.sql.Template;
import org.libreplan.business.requirements.entities.CriterionRequirement;
import org.libreplan.business.requirements.entities.DirectCriterionRequirement;
import org.libreplan.business.requirements.entities.IndirectCriterionRequirement;
import org.libreplan.business.resources.entities.Criterion;
import org.libreplan.business.resources.entities.ResourceEnum;
import org.libreplan.business.templates.entities.OrderElementTemplate;
/**
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
* @author Diego Pino Garcia <dpino@igalia.com>
*/
// <OrderElement, OrderLine, OrderLineGroup>
public abstract class CriterionRequirementHandler<T, S, R> {
// Operations to add a criterionRequirement
public void propagateDirectCriterionRequirementAddition(T orderElement,
CriterionRequirement directo) {
propagateIndirectCriterionRequirement(orderElement,
(DirectCriterionRequirement) directo);
}
protected void propagateIndirectCriterionRequirement(T orderElement,
CriterionRequirement direct) {
if (isOrderLine(orderElement)) {
propagateIndirectCriterionRequirementToOrderLineChildren(
toOrderLine(orderElement),
(DirectCriterionRequirement) direct);
} else {
propagateIndirectCriterionRequirementToOrderLineGroupChildren(
toOrderLineGroup(orderElement),
(DirectCriterionRequirement) direct);
}
}
protected void propagateIndirectCriterionRequirementToOrderLineGroupChildren(
R orderLineGroup, DirectCriterionRequirement parent) {
Criterion criterion = parent.getCriterion();
for (T child : getOrderLineGroupChildren(orderLineGroup)) {
IndirectCriterionRequirement indirect = IndirectCriterionRequirement
.create(parent, criterion);
addIndirectCriterionRequirement(child, indirect);
propagateIndirectCriterionRequirement(child, parent);
}
}
protected void propagateIndirectCriterionRequirementToOrderLineChildren(
S orderLine, DirectCriterionRequirement parent) {
for (HoursGroup hoursGroup : getHoursGroups(orderLine)) {
hoursGroup.updateMyCriterionRequirements();
}
}
protected abstract boolean isOrderLine(T orderElement);
protected abstract S toOrderLine(T orderElement);
protected abstract R toOrderLineGroup(T orderElement);
protected abstract List<HoursGroup> getHoursGroups(S orderline);
protected abstract List<T> getOrderLineGroupChildren(R orderLineGroup);
public boolean canAddCriterionRequirement(T orderElement,
CriterionRequirement newRequirement) {
List<T> listOrderElements = getAllChildren(orderElement);
listOrderElements.add(orderElement);
for (T element : listOrderElements) {
if (existSameCriterionRequirement(element, newRequirement)) {
return false;
}
}
return true;
}
protected abstract List<T> getAllChildren(T orderElement);
public boolean existSameCriterionRequirement(T orderElement,
CriterionRequirement newRequirement) {
if (isOrderLine(orderElement)) {
return existSameCriterionRequirementIntoOrderLine(toOrderLine(orderElement), newRequirement);
} else {
return existSameCriterionRequirementIntoOrderElement(orderElement,
newRequirement);
}
}
public boolean existSameCriterionRequirementIntoOrderLine(S orderLine,
CriterionRequirement newRequirement) {
if (existSameCriterionRequirementIntoOrderElement(toOrderElement(orderLine),
newRequirement)) {
return true;
}
for (HoursGroup hoursGroup : getHoursGroups(orderLine)) {
if (hoursGroup.existSameCriterionRequirement(newRequirement)) {
return true;
}
}
return false;
}
protected abstract T toOrderElement(S orderLine);
public boolean existSameCriterionRequirementIntoOrderElement(
T orderElement, CriterionRequirement newRequirement) {
Criterion criterion = newRequirement.getCriterion();
for (CriterionRequirement requirement : getCriterionRequirements(orderElement)) {
if (requirement.getCriterion().equals(criterion)) {
return true;
}
}
return false;
}
protected abstract Set<CriterionRequirement> getCriterionRequirements(
T orderElement);
/*
* Operations to set the valid value of a criterion Requirements and the
* criterion Requirement of its children.
*/
public void propagateValidCriterionRequirement(T orderElement,
DirectCriterionRequirement parent, boolean valid) {
if (isOrderLine(orderElement)) {
setValidCriterionRequirementChildrenAsOrderLine(
toOrderLine(orderElement), parent, valid);
} else {
setValidCriterionRequirementChildrenAsOrderLineGroup(
toOrderLineGroup(orderElement), parent, valid);
}
}
protected void setValidCriterionRequirementChildrenAsOrderLineGroup(
R orderLineGroup, DirectCriterionRequirement parent,
boolean valid) {
for (T child : getOrderLineGroupChildren(orderLineGroup)) {
IndirectCriterionRequirement indirect = findIndirectRequirementByParent(
getIndirectCriterionRequirement(child), parent);
if (indirect != null) {
indirect.setValid(valid);
}
propagateValidCriterionRequirement(child, parent, valid);
}
}
protected abstract Set<IndirectCriterionRequirement> getIndirectCriterionRequirement(
T orderElement);
protected void setValidCriterionRequirementChildrenAsOrderLine(S orderLine,
DirectCriterionRequirement parent, boolean valid) {
for (HoursGroup hoursGroup : getHoursGroups(orderLine)) {
IndirectCriterionRequirement indirect = findIndirectRequirementByParent(
hoursGroup.getIndirectCriterionRequirement(), parent);
if (indirect != null) {
indirect.setValid(valid);
}
}
}
/*
* Operation to update the criterions requirements of the orderElement and
* its children
*/
public void propagateRemoveCriterionRequirement(T orderElement,
DirectCriterionRequirement parent) {
if (isOrderLine(orderElement)) {
removeIndirectCriterionRequirementAsOrderLine(toOrderLine(orderElement), parent);
} else {
removeIndirectCriterionRequirement(toOrderLineGroup(orderElement),
parent);
}
}
protected void removeIndirectCriterionRequirement(
R orderLineGroup, DirectCriterionRequirement parent) {
for (T child : getOrderLineGroupChildren(orderLineGroup)) {
IndirectCriterionRequirement indirect = findIndirectRequirementByParent(
getIndirectCriterionRequirement(child), parent);
if (indirect != null) {
propagateRemoveCriterionRequirement(child, parent);
removeCriterionRequirement(child, indirect);
}
}
}
protected abstract void removeCriterionRequirement(T orderElement,
CriterionRequirement criterionRequirement);
protected void removeIndirectCriterionRequirementAsOrderLine(S orderLine,
DirectCriterionRequirement parent) {
for (HoursGroup hoursGroup : getHoursGroups(orderLine)) {
IndirectCriterionRequirement indirect = findIndirectRequirementByParent(
hoursGroup.getIndirectCriterionRequirement(), parent);
if (indirect != null) {
hoursGroup.removeCriterionRequirement(indirect);
}
}
}
public void removeDirectCriterionRequirement(T orderElement, DirectCriterionRequirement criterionRequirement){
propagateRemoveCriterionRequirement(orderElement, criterionRequirement);
removeCriterionRequirement(orderElement, criterionRequirement);
}
protected abstract List<HoursGroup> getOrderLineHoursGroups(S orderLine);
/*
* Operation to update the criterions requirements of the orderElement and
* its children
*/
public void propagateUpdateCriterionRequirements(T orderElement) {
if (orderElement instanceof OrderLine) {
updateCriterionRequirementsIntoOrderLine(toOrderLine(orderElement));
} else {
updateCriterionRequirementsIntoOrderLineGroup(toOrderLineGroup(orderElement));
}
}
private void updateCriterionRequirementsIntoOrderLineGroup(
R orderLineGroup) {
for (T child : getOrderLineGroupChildren(orderLineGroup)) {
updateMyCriterionRequirements(child);
propagateUpdateCriterionRequirements(child);
}
}
protected abstract List<T> getChildren(T orderElement);
public void updateCriterionRequirementsIntoOrderLine(S orderLine) {
for (HoursGroup hoursGroup : getHoursGroups(orderLine)) {
hoursGroup.updateMyCriterionRequirements();
}
}
public void propagateIndirectCriterionRequirementsKeepingValid(S orderLine) {
for (HoursGroup hoursGroup : getHoursGroups(orderLine)) {
hoursGroup.propagateIndirectCriterionRequirementsKeepingValid();
}
}
void transformDirectToIndirectIfNeeded(T orderElement,
Set<IndirectCriterionRequirement> currents) {
for (DirectCriterionRequirement direct : getDirectCriterionRequirement(orderElement)) {
IndirectCriterionRequirement indirect = findIndirectRequirementByCriterion(
currents, direct.getCriterion());
if (indirect != null) {
removeDirectCriterionRequirement(orderElement, direct);
}
}
}
protected abstract Set<DirectCriterionRequirement> getDirectCriterionRequirement(
T orderElement);
void addNewsIndirects(T orderElement,
Set<IndirectCriterionRequirement> currents) {
Set<IndirectCriterionRequirement> indirects = getIndirectCriterionRequirement(orderElement);
for (IndirectCriterionRequirement current : currents) {
if (!indirects.contains(current)) {
basicAddCriterionRequirement(orderElement, current);
}
}
}
protected abstract void basicAddCriterionRequirement(T orderElement,
CriterionRequirement criterionRequirement);
void removeOldIndirects(T orderElement,
Set<IndirectCriterionRequirement> currents) {
for (IndirectCriterionRequirement indirect : getIndirectCriterionRequirement(orderElement)) {
if (!currents.contains(indirect)) {
removeCriterionRequirement(orderElement, indirect);
}
}
}
void transformDirectToIndirectIfNeeded(HoursGroup hoursGroup,
Set<IndirectCriterionRequirement> currents) {
for (DirectCriterionRequirement direct : hoursGroup
.getDirectCriterionRequirement()) {
IndirectCriterionRequirement indirect = findIndirectRequirementByCriterion(
currents, direct.getCriterion());
if (indirect != null) {
hoursGroup.removeCriterionRequirement(direct);
}
}
}
void addNewsIndirects(HoursGroup hoursGroup,
Set<IndirectCriterionRequirement> currents) {
Set<IndirectCriterionRequirement> indirects = hoursGroup
.getIndirectCriterionRequirement();
for (IndirectCriterionRequirement current : currents) {
if (!indirects.contains(current)) {
hoursGroup.addCriterionRequirement(current);
}
}
}
void removeOldIndirects(HoursGroup hoursGroup,
Set<IndirectCriterionRequirement> currents) {
for (IndirectCriterionRequirement indirect : hoursGroup
.getIndirectCriterionRequirement()) {
if (!currents.contains(indirect)) {
hoursGroup.removeCriterionRequirement(indirect);
}
}
}
Set<IndirectCriterionRequirement> getCurrentIndirectRequirements(
Set<IndirectCriterionRequirement> oldIndirects,
Set<CriterionRequirement> requirementsParent) {
Set<IndirectCriterionRequirement> currentIndirects = new HashSet<IndirectCriterionRequirement>();
for (CriterionRequirement requirement : requirementsParent) {
IndirectCriterionRequirement indirect = getCurrentIndirectRequirement(
oldIndirects, requirement);
currentIndirects.add(indirect);
}
return currentIndirects;
}
IndirectCriterionRequirement getCurrentIndirectRequirement(
Set<IndirectCriterionRequirement> oldIndirects,
CriterionRequirement requirement) {
IndirectCriterionRequirement indirect;
DirectCriterionRequirement parent;
boolean valid = true;
if (requirement instanceof DirectCriterionRequirement) {
parent = (DirectCriterionRequirement) requirement;
} else {
parent = ((IndirectCriterionRequirement) requirement).getParent();
valid = ((IndirectCriterionRequirement) requirement).isValid();
}
indirect = findIndirectRequirementByParent(oldIndirects, parent);
if (indirect == null) {
indirect = IndirectCriterionRequirement.create(parent, requirement
.getCriterion());
indirect.setValid(valid);
}
return (IndirectCriterionRequirement) indirect;
}
private IndirectCriterionRequirement findIndirectRequirementByParent(
Set<IndirectCriterionRequirement> indirects,
DirectCriterionRequirement newParent) {
for (IndirectCriterionRequirement requirement : indirects) {
if (requirement.getParent().equals(newParent)) {
return requirement;
}
}
return null;
}
private IndirectCriterionRequirement findIndirectRequirementByCriterion(
Set<IndirectCriterionRequirement> indirects, Criterion criterion) {
for (IndirectCriterionRequirement requirement : indirects) {
if (requirement.getCriterion().equals(criterion)) {
return requirement;
}
}
return null;
}
/*
* Operation to create and add to a orderElement new criterion requirements
* that it is copied of the criterion requirements of other orderElement
*/
protected void copyRequirementToOrderLineGroup(S orderLine, R orderLineGroup) {
// copy the directCriterionRequirement
for (DirectCriterionRequirement newRequirement :
copyDirectRequirements(getDirectCriterionRequirementFromOrderLine(orderLine))) {
basicAddCriterionRequirementIntoOrderLineGroup(orderLineGroup, newRequirement);
}
// copy the IndirectCriterionRequirement
for (IndirectCriterionRequirement newRequirement :
copyIndirectRequirements(getIndirectCriterionRequirementFromOrderLine(orderLine))) {
basicAddCriterionRequirementIntoOrderLineGroup(orderLineGroup, newRequirement);
}
}
protected abstract Set<DirectCriterionRequirement> getDirectCriterionRequirementFromOrderLine(S orderLine);
protected abstract Set<IndirectCriterionRequirement> getIndirectCriterionRequirementFromOrderLine(S orderLine);
protected abstract void basicAddCriterionRequirementIntoOrderLineGroup(
R orderLineGroup,
CriterionRequirement criterionRequirement);
protected void copyRequirementToOrderLine(R orderLineGroup, S orderLine) {
// copy the directCriterionRequirement
for (DirectCriterionRequirement newRequirement : copyDirectRequirements(
getDirectCriterionRequirementFromOrderLineGroup(orderLineGroup))) {
addDirectCriterionRequirementToOrderLine(orderLine, newRequirement);
}
// copy the IndirectCriterionRequirement
for (IndirectCriterionRequirement newRequirement : copyIndirectRequirements(
getIndirectCriterionRequirementFromOrderLineGroup(orderLineGroup))) {
addIndirectCriterionRequirementToOrderLine(orderLine, newRequirement);
propagateIndirectCriterionRequirementToOrderLineChildren(orderLine,
newRequirement.getParent());
}
}
protected abstract Set<DirectCriterionRequirement>
getDirectCriterionRequirementFromOrderLineGroup(R orderLineGroup);
protected abstract void addIndirectCriterionRequirementToOrderLine(S orderLine, IndirectCriterionRequirement indirect);
protected abstract Set<IndirectCriterionRequirement> getIndirectCriterionRequirementFromOrderLineGroup(R orderLineGroup);
private Set<DirectCriterionRequirement> copyDirectRequirements(
Set<DirectCriterionRequirement> collection) {
Set<DirectCriterionRequirement> result = new HashSet<DirectCriterionRequirement>();
for (DirectCriterionRequirement requirement : collection) {
DirectCriterionRequirement newRequirement = DirectCriterionRequirement
.create(requirement.getCriterion());
result.add(newRequirement);
}
return result;
}
private Set<IndirectCriterionRequirement> copyIndirectRequirements(
Set<IndirectCriterionRequirement> collection) {
Set<IndirectCriterionRequirement> result = new HashSet<IndirectCriterionRequirement>();
for (IndirectCriterionRequirement requirement : collection) {
DirectCriterionRequirement parent = requirement.getParent();
IndirectCriterionRequirement newRequirement = IndirectCriterionRequirement
.create(parent, requirement.getCriterion());
newRequirement.setValid(requirement.isValid());
result.add(newRequirement);
}
return result;
}
Set<CriterionRequirement> getRequirementWithSameResourType(
Set<CriterionRequirement> requirements, ResourceEnum resourceType) {
Set<CriterionRequirement> result = new HashSet<CriterionRequirement>();
for (CriterionRequirement requirement : requirements) {
ResourceEnum resourceTypeParent = requirement.getCriterion()
.getType().getResource();
if (resourceTypeParent.equals(resourceType)) {
result.add(requirement);
}
}
return result;
}
/**
* Filters {@link DirectCriterionRequirement} from criterionRequirements
*
* @param criterionRequirements
* @return
*/
public Set<DirectCriterionRequirement> getDirectCriterionRequirement(
Set<CriterionRequirement> criterionRequirements) {
Set<DirectCriterionRequirement> list = new HashSet<DirectCriterionRequirement>();
for (CriterionRequirement criterionRequirement : criterionRequirements) {
if (criterionRequirement instanceof DirectCriterionRequirement) {
list.add((DirectCriterionRequirement) criterionRequirement);
}
}
return list;
}
public Set<IndirectCriterionRequirement> getIndirectCriterionRequirement(
Set<CriterionRequirement> criterionRequirements) {
Set<IndirectCriterionRequirement> list = new HashSet<IndirectCriterionRequirement>();
for (CriterionRequirement criterionRequirement : criterionRequirements) {
if (criterionRequirement instanceof IndirectCriterionRequirement) {
list.add((IndirectCriterionRequirement) criterionRequirement);
}
}
return list;
}
public void addCriterionRequirement(T orderElement,
CriterionRequirement criterionRequirement) {
if (criterionRequirement instanceof DirectCriterionRequirement) {
addDirectCriterionRequirement(orderElement, (DirectCriterionRequirement) criterionRequirement);
} else { // criterionRequirement instanceof IndirectCriterionRequirement
addIndirectCriterionRequirement(orderElement, (IndirectCriterionRequirement) criterionRequirement);
}
}
public void addDirectCriterionRequirement(T orderElement,
CriterionRequirement newRequirement) {
if (canAddCriterionRequirement(orderElement,
newRequirement)) {
basicAddCriterionRequirement(orderElement, newRequirement);
propagateDirectCriterionRequirementAddition(orderElement,
newRequirement);
} else {
throw new IllegalStateException(
_("The criterion already exists into another task"));
}
}
public void addIndirectCriterionRequirement(T orderElement,
IndirectCriterionRequirement criterionRequirement) {
basicAddCriterionRequirement(orderElement, criterionRequirement);
}
protected void addDirectCriterionRequirementToOrderLine(S orderLine, DirectCriterionRequirement direct) {
addDirectCriterionRequirement(toOrderElement(orderLine), direct);
}
public void updateMyCriterionRequirements(T orderElement) {
final T parent = getParent(orderElement);
if (parent != null) {
Set<CriterionRequirement> requirementsParent = getCriterionRequirements(parent);
Set<IndirectCriterionRequirement> currentIndirects = getCurrentIndirectRequirements(
getIndirectCriterionRequirement(orderElement),
requirementsParent);
transformDirectToIndirectIfNeeded(orderElement, currentIndirects);
removeOldIndirects(orderElement, currentIndirects);
addNewsIndirects(orderElement, currentIndirects);
}
}
protected abstract T getParent(T orderElement);
/**
* Propagates {@link IndirectCriterionRequirement} for an
* {@link OrderElement} or {@link OrderElementTemplate} preserving its valid
* attribute
*
*/
public void propagateIndirectCriterionRequirementsForOrderLineGroupKeepingValid(
R orderLineGroup,
DirectCriterionRequirement parent) {
copyIndirectCriterionRequirementsFromOriginalToOrderLineGroupChildren(
orderLineGroup, parent);
copyIndirectCriterionRequirementsFromOriginalToHoursGroup(
orderLineGroup, parent);
}
public abstract void copyIndirectCriterionRequirementsFromOriginalToOrderLineGroupChildren(
R orderLineGroup,
DirectCriterionRequirement parent);
public void copyIndirectCriterionRequirementsFromOriginalToHoursGroup(
R orderLineGroup,
DirectCriterionRequirement parent) {
final List<T> orderElements = getOrderLineGroupChildren(orderLineGroup);
final Criterion criterion = parent.getCriterion();
final Set<IndirectCriterionRequirement> originalIndirectCriterionRequirements = parent
.getOrigin().getChildren();
final Map<HoursGroup, Map<Criterion, Boolean>> mapHoursGroup =
createHoursGroupCriterionMap(originalIndirectCriterionRequirements);
for (T each: orderElements) {
IndirectCriterionRequirement indirect = IndirectCriterionRequirement
.create(parent, criterion);
if (isOrderLine(each)) {
for (HoursGroup hoursGroup: myHoursGroups(toOrderLine(each))) {
Map<Criterion, Boolean> criterionMap = mapHoursGroup.get(hoursGroup.getOrigin());
if (criterionMap != null) {
final Boolean valid = criterionMap.get(indirect.getCriterion());
indirect.setValid(valid);
}
hoursGroup.addCriterionRequirement(indirect);
}
}
if (isOrderLineGroup(each)) {
copyIndirectCriterionRequirementsFromOriginalToHoursGroup(
toOrderLineGroup(each), parent);
}
}
}
protected abstract boolean isOrderLineGroup(T orderElement);
protected abstract Collection<HoursGroup> myHoursGroups(S orderline);
/**
* Creates a mapping between {@link HoursGroup} and a tuple (criterion,
* boolean) from a list of {@link IndirectCriterionRequirement}
*
* The valid value of an {@link IndirectCriterionRequirement} can be later
* retrieve knowing its {@link HoursGroup} and its {@link Criterion}
*
* This data structure is used to keep the original valid value from
* {@link IndirectCriterionRequirement} when copying an {@link Order} to a
* {@link Template} or vice-versa
*
* @param indirects
* @return
*/
private Map<HoursGroup, Map<Criterion, Boolean>> createHoursGroupCriterionMap(
Set<IndirectCriterionRequirement> indirects) {
Map<HoursGroup, Map<Criterion, Boolean>> result =
new HashMap<HoursGroup, Map<Criterion, Boolean>>();
for (IndirectCriterionRequirement each: indirects) {
final HoursGroup hoursGroup = each.getHoursGroup();
if (hoursGroup != null) {
Map<Criterion, Boolean> value = result.get(hoursGroup);
if (value == null) {
value = new HashMap<Criterion, Boolean>();
}
value.put(each.getCriterion(), each.isValid());
result.put(hoursGroup, value);
}
}
return result;
}
}