/*
* 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/>.
*/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.libreplan.web.resources.criterion;
import static org.libreplan.business.common.exceptions.ValidationException.invalidValue;
import static org.libreplan.web.I18nHelper._;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.resources.entities.Criterion;
import org.libreplan.business.resources.entities.CriterionType;
import org.zkoss.ganttz.util.MutableTreeModel;
import org.zkoss.zul.TreeModel;
/**
* Model for a the {@link Criterion} tree for a {@link CriterionType} <br />
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
*/
public class CriterionTreeModel implements ICriterionTreeModel {
private MutableTreeModel<CriterionDTO> tree;
private final CriterionType criterionType;
private final CriterionDTO criterionRootDTO;
private static MutableTreeModel<CriterionDTO> createTreeFrom(CriterionDTO criterionRootDTO) {
List<CriterionDTO> criterionDTOs = criterionRootDTO.getChildren();
MutableTreeModel<CriterionDTO> treeModel = MutableTreeModel
.create(CriterionDTO.class);
CriterionDTO parent = treeModel.getRoot();
treeModel.add(parent, criterionDTOs);
addChildren(treeModel, criterionDTOs);
return treeModel;
}
private static List<CriterionDTO> getCriterionDTOs(
CriterionType criterionType,CriterionDTO criterionRootDTO){
List<Criterion> criterions = Criterion
.sortByName(getDirectCriterions(criterionType));
return CriterionDTO.asListDTO(criterions, criterionRootDTO);
}
private static Set<Criterion> getDirectCriterions(CriterionType criterionType){
Set<Criterion> criterions = new HashSet<Criterion>();
for(Criterion criterion : criterionType.getCriterions()){
if(criterion.getParent() == null){
criterions.add(criterion);
}
}
return criterions;
}
private static void addChildren(MutableTreeModel<CriterionDTO> treeModel,
List<CriterionDTO> criterions) {
for (CriterionDTO criterion : criterions) {
treeModel.add(criterion, criterion.getChildren());
addChildren(treeModel, criterion.getChildren());
}
}
public CriterionType getCriterionType(){
return criterionType;
}
public CriterionTreeModel(CriterionType criterionType) {
this.criterionType = criterionType ;
criterionRootDTO = new CriterionDTO();
List<CriterionDTO> listDTOs = getCriterionDTOs(criterionType,criterionRootDTO);
criterionRootDTO.setChildren(listDTOs);
tree = this.createTreeFrom(criterionRootDTO);
}
public TreeModel asTree() {
return tree;
}
public void addCriterion(String name) {
CriterionDTO newCriterionDTO = createNewCriterion(name);
newCriterionDTO.setActive(criterionType.isEnabled());
addToTree(tree.getRoot(), newCriterionDTO,0);
addCriterionAtCriterionType(newCriterionDTO,0);
}
public void addCriterionAt(CriterionDTO node,String name) {
CriterionDTO newCriterion = createNewCriterion(name);
newCriterion.setActive(criterionType.isEnabled());
addToTree(node,newCriterion,0);
addCriterionAtCriterion(node, newCriterion,0);
}
private CriterionDTO createNewCriterion(String name) {
CriterionDTO newCriterion = new CriterionDTO();
newCriterion.setName(_(name));
Criterion criterion = Criterion.create(criterionType);
newCriterion.setCriterion(criterion);
return newCriterion;
}
private void addToTree(CriterionDTO parentNode, CriterionDTO elementToAdd,int position) {
tree.add(parentNode,position,Arrays.asList(elementToAdd));
addChildren(tree,Arrays.asList(elementToAdd));
}
private void addToTree(CriterionDTO parentNode,
List<CriterionDTO> elementsToAdd) {
tree.add(parentNode, elementsToAdd);
addChildren(tree, elementsToAdd);
}
private void addCriterionAtCriterionType(CriterionDTO elementToAdd,int position) {
elementToAdd.setParent(criterionRootDTO);
criterionRootDTO.getChildren().add(position,elementToAdd);
}
private void addCriterionAtCriterion(CriterionDTO parent, CriterionDTO elementToAdd,int position) {
elementToAdd.setParent(parent);
parent.getChildren().add(position,elementToAdd);
}
@Override
public void up(CriterionDTO node) {
CriterionDTO parent = asCriterion(((CriterionDTO)tree.getParent(node)));
int pos = parent.up(node);
tree.up(node);
if((pos == 0)&&(!parent.equals(criterionRootDTO))){
upGranParent(node);
}
}
private void upGranParent(CriterionDTO node){
CriterionDTO parent = tree.getParent(node);
CriterionDTO grandParent = tree.getParent(parent);
int position = getChildren(grandParent).indexOf(parent);
if(tree.isRoot(grandParent)){
this.moveToRoot(node,position);
}else{
this.moveImpl(node, grandParent, position);
}
}
@Override
public void down(CriterionDTO node) {
CriterionDTO parent = asCriterion(((CriterionDTO)tree.getParent(node)));
int pos = parent.down(node);
tree.down(node);
if((pos == parent.getChildren().size() - 1)
&&(!parent.equals(criterionRootDTO))){
downGranParent(node);
}
}
private void downGranParent(CriterionDTO node){
CriterionDTO parent = tree.getParent(node);
CriterionDTO grandParent = tree.getParent(parent);
int position = getChildren(grandParent).indexOf(parent)+1;
if(tree.isRoot(grandParent)){
this.moveToRoot(node,position);
}else{
this.moveImpl(node, grandParent, position);
}
}
@Override
public void indent(CriterionDTO nodeToIndent) {
CriterionDTO parentOfSelected = tree.getParent(nodeToIndent);
int position = getChildren(parentOfSelected).indexOf(nodeToIndent);
if (position == 0) {
return;
}
CriterionDTO destination = (CriterionDTO) getChildren(parentOfSelected)
.get(position - 1);
moveImpl(nodeToIndent, destination, getChildren(destination).size());
}
@Override
public void unindent(CriterionDTO nodeToUnindent) {
CriterionDTO parent = tree.getParent(nodeToUnindent);
if (tree.isRoot(parent)) {
return;
}
CriterionDTO destination = tree.getParent(parent);
moveImpl(nodeToUnindent, destination, getChildren(destination).indexOf(
parent) + 1);
}
private List<CriterionDTO> getChildren(CriterionDTO node) {
List<CriterionDTO> result = new ArrayList<CriterionDTO>();
final int childCount = tree.getChildCount(node);
for (int i = 0; i < childCount; i++) {
result.add(tree.getChild(node, i));
}
return result;
}
@Override
public void move(CriterionDTO toBeMoved, CriterionDTO destination,int position) {
if(isGreatInHierarchy(toBeMoved,destination)){
return;
}
if(criterionType.allowHierarchy()){
moveImpl(toBeMoved, destination,position);
}
}
@Override
public void moveToRoot(CriterionDTO toBeMoved,int position) {
moveImpl(toBeMoved, tree.getRoot(),position);
}
private void moveImpl(CriterionDTO toBeMoved, CriterionDTO destination,int position) {
if (getChildren(destination).contains(toBeMoved)) {
return;// it's already moved
}
removeNodeImpl(toBeMoved);
moveCriterion(toBeMoved,destination,position);
addToTree(destination, toBeMoved,position);
}
private void moveCriterion(CriterionDTO toBeMoved, CriterionDTO destination,int position){
//Add at CriterionType or at a criterion
if(tree.isRoot(destination)){
toBeMoved.setParent(criterionRootDTO);
criterionRootDTO.getChildren().add(position,toBeMoved);
}else{
toBeMoved.setParent(destination);
destination.getChildren().add(position,toBeMoved);
}
}
private CriterionDTO asCriterion(CriterionDTO node) {
if (tree.isRoot(node)) {
return criterionRootDTO;
}
return (CriterionDTO) node;
}
public void removeNode(CriterionDTO node) {
removeNodeImpl(node);
}
private void removeNodeImpl(CriterionDTO criterionDto) {
if (criterionDto == tree.getRoot()) {
return;
}
CriterionDTO parentDto = criterionDto.getParent();
if (parentDto == criterionRootDTO) {
criterionRootDTO.getChildren().remove(criterionDto);
} else {
parentDto.getChildren().remove(criterionDto);
}
Criterion parent = criterionDto.getCriterion().getParent();
if ((parent != null) && (!parent.getChildren().isEmpty())) {
parent.getChildren().remove(criterionDto.getCriterion());
}
tree.remove(criterionDto);
}
public int[] getPath(Criterion criterion) {
return tree.getPath(tree.getRoot(), criterion);
}
public void flattenTree(){
List<CriterionDTO> criterions = copyCriterions(criterionRootDTO.getChildren());
for(CriterionDTO criterion : criterions){
flattenTree(criterion);
}
}
private void flattenTree(CriterionDTO criterion){
if(criterion.getChildren().size()>0){
List<CriterionDTO> criterions = copyCriterions(criterion.getChildren());
for(CriterionDTO criterionChild : criterions){
flattenTree(criterionChild);
}
}
moveToRoot(criterion,criterionRootDTO.getChildren().size());
}
private List<CriterionDTO> copyCriterions(List<CriterionDTO> criterions){
List<CriterionDTO> newCriterions = new ArrayList<CriterionDTO>();
for(CriterionDTO criterion : criterions){
newCriterions.add(criterion);
}
return newCriterions;
}
public void thereIsOtherWithSameNameAndType(String name)
throws ValidationException{
thereIsOtherWithSameNameAndType(name,criterionRootDTO.getChildren());
}
private void thereIsOtherWithSameNameAndType(String name,List<CriterionDTO> criterions)
throws ValidationException{
for(CriterionDTO criterion : criterions){
if(criterion.getName().equals(name)){
throw new ValidationException(invalidValue(
_("Already exists another "
+ "criterion with the same name"), "name",
criterion.getName(), criterion));
}
thereIsOtherWithSameNameAndType(name,criterion.getChildren());
}
}
public void validateNameNotEmpty(String name)
throws ValidationException{
if(name.isEmpty()){
throw new ValidationException(
invalidValue(_("Name of criterion is empty."), "name",
"",criterionType));
}
}
public void updateEnabledCriterions(boolean isChecked){
List<CriterionDTO> list = criterionRootDTO.getChildren();
updateEnabledCriterions(isChecked,list);
updateEnabledCriterionsTree(isChecked,getChildren(tree.getRoot()));
}
public void updateEnabledCriterions(boolean isChecked,CriterionDTO criterion){
List<CriterionDTO> list = criterion.getChildren();
updateEnabledCriterions(isChecked,list);
updateEnabledCriterionsTree(isChecked,list);
}
private void updateEnabledCriterions(boolean isChecked,
List<CriterionDTO> criterions){
for(CriterionDTO criterion : criterions){
criterion.setActive(isChecked);
if(criterion.getChildren().size() > 0){
updateEnabledCriterions(isChecked,criterion.getChildren());
}
}
}
private void updateEnabledCriterionsTree(boolean isChecked,
List<CriterionDTO> criterions){
for(CriterionDTO criterion: criterions){
criterion.setActive(isChecked);
tree.replace(criterion, criterion);
addToTree(criterion, criterion.getChildren());
updateEnabledCriterionsTree(isChecked,getChildren(criterion));
}
}
@Override
public void saveCriterions(CriterionType criterionType){
updateCriterions(criterionRootDTO.getChildren());
}
public void updateCriterions(List<CriterionDTO> criterionDTOs){
for(CriterionDTO criterionDTO : criterionDTOs){
updateDataCriterion(criterionDTO);
updateParent(criterionDTO);
updateCriterions(criterionDTO.getChildren());
}
}
private void updateParent(CriterionDTO criterionDTO){
if(!criterionDTO.isNewObject()){
updateOldParent(criterionDTO);
}
updateNewParent(criterionDTO);
}
private void updateOldParent(CriterionDTO criterionDTO){
Criterion oldParent = criterionDTO.getCriterion().getParent();
if(oldParent != null){
oldParent.getChildren().remove(criterionDTO.getCriterion());
}
}
private void updateNewParent(CriterionDTO criterionDTO){
Criterion newParent = criterionDTO.getParent().getCriterion();
criterionDTO.getCriterion().setParent(newParent);
if(newParent == null){
criterionType.getCriterions().add(criterionDTO.getCriterion());
}else{
newParent.getChildren().add(criterionDTO.getCriterion());
}
}
private void updateDataCriterion(CriterionDTO criterionDTO){
Criterion criterion = criterionDTO.getCriterion();
criterion.setName(criterionDTO.getName());
criterion.setActive(criterionDTO.isActive());
}
private boolean isGreatInHierarchy(CriterionDTO parent,CriterionDTO child){
return find(child,getChildren(parent));
}
private boolean find(CriterionDTO child,List<CriterionDTO> children){
if(children.indexOf(child) >= 0) {
return true;
}
for(CriterionDTO criterionDTO : children){
return find(child,getChildren(criterionDTO));
}
return false;
}
public void regenerateCodeForUnsavedCriteria(int numberOfDigits) {
regenerateCodeForUnsavedCriteria(criterionRootDTO.getChildren(),
numberOfDigits);
}
private void regenerateCodeForUnsavedCriteria(
List<CriterionDTO> criterionDTOs, int numberOfDigits) {
for (CriterionDTO criterionDTO : criterionDTOs) {
Criterion criterion = criterionDTO.getCriterion();
if ((criterion.getCode() == null)
|| (criterion.getCode().isEmpty())
|| (!criterion.getCode()
.startsWith(criterionType.getCode()))) {
criterionType.setGenerateCode(criterion, numberOfDigits);
}
regenerateCodeForUnsavedCriteria(criterionDTO.getChildren(),
numberOfDigits);
}
}
}