/*
* 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.
* Copyright (C) 2010-2011 WirelessGalicia, 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/>.
*/
/**
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
*/
package org.libreplan.business.qualityforms.entities;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.Validate;
import org.hibernate.NonUniqueResultException;
import javax.validation.constraints.AssertTrue;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.Valid;
import org.libreplan.business.advance.entities.AdvanceType;
import org.libreplan.business.common.BaseEntity;
import org.libreplan.business.common.IHumanIdentifiable;
import org.libreplan.business.common.Registry;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.qualityforms.daos.IQualityFormDAO;
public class QualityForm extends BaseEntity implements IHumanIdentifiable{
public static final String ADVANCE_TYPE_PREFIX = "QF: ";
public static QualityForm create() {
QualityForm qualityForm = new QualityForm();
qualityForm.setNewObject(true);
return qualityForm;
}
public static QualityForm create(String name, String description) {
QualityForm qualityForm = new QualityForm(name, description);
qualityForm.setNewObject(true);
return qualityForm;
}
protected QualityForm() {
}
private QualityForm(String name, String description) {
this.name = name;
this.description = description;
}
private String name;
private String description;
private QualityFormType qualityFormType = QualityFormType.getDefault();
private List<QualityFormItem> qualityFormItems = new ArrayList<QualityFormItem>();
private Boolean reportAdvance = false;
private AdvanceType advanceType;
@NotEmpty(message = "quality form name not specified")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@NotNull(message = "quality form type not specified")
public QualityFormType getQualityFormType() {
return qualityFormType;
}
public void setQualityFormType(QualityFormType qualityFormType) {
Validate.notNull(qualityFormType);
if (changeFromByItemsToByPercentage(qualityFormType)) {
updatePercentageByPercentage();
}
this.qualityFormType = qualityFormType;
updateAndSortQualityFormItem();
}
@Valid
public List<QualityFormItem> getQualityFormItems() {
return Collections.unmodifiableList(qualityFormItems);
}
void setQualityFormItems(List<QualityFormItem> qualityFormItems) {
this.qualityFormItems = qualityFormItems;
}
public boolean addQualityFormItemOnTop(QualityFormItem qualityFormItem) {
if (qualityFormItem != null) {
Integer position = 0;
qualityFormItem.setPosition(position);
qualityFormItems.add(position, qualityFormItem);
updateAndSortQualityFormItem();
}
return false;
}
public void removeQualityFormItem(QualityFormItem qualityFormItem) {
qualityFormItems.remove(qualityFormItem);
updateAndSortQualityFormItem();
}
public void updateAndSortQualityFormItem() {
if (qualityFormType != null) {
if (this.qualityFormType.equals(QualityFormType.BY_PERCENTAGE)) {
updateAndSortQualityFormItemPositionsByPercentage();
} else {
updateAndSortQualityFormItemPositionsByItems();
updatePercentageByItems();
}
}
}
public void moveQualityFormItem(QualityFormItem qualityFormItem,
Integer newPosition) {
if (checkValidPosition(newPosition)) {
qualityFormItems.remove(qualityFormItem);
qualityFormItems.add(newPosition, qualityFormItem);
updateAndSortQualityFormItemPositionsByItems();
}
}
public QualityFormItem findQualityFormItemWithDuplicateName() {
List<QualityFormItem> items = new ArrayList<QualityFormItem>(
qualityFormItems);
for (int i = 0; i < items.size(); i++) {
for (int j = i + 1; j < items.size(); j++) {
if ((items.get(j).getName() != null)
&& (items.get(i).getName() != null)
&& (items.get(j).getName().equals(items.get(i)
.getName()))) {
return items.get(j);
}
}
}
return null;
}
public QualityFormItem findQualityFormItemWithDuplicatePercentage() {
List<QualityFormItem> items = new ArrayList<QualityFormItem>(
qualityFormItems);
for (int i = 0; i < items.size(); i++) {
for (int j = i + 1; j < items.size(); j++) {
if ((items.get(j).getPercentage() != null)
&& (items.get(i).getPercentage() != null)
&& (items.get(j).getPercentage().equals(items.get(i)
.getPercentage()))) {
return items.get(j);
}
}
}
return null;
}
@SuppressWarnings("unused")
@AssertTrue(message = "Quality form name is already being used")
public boolean isUniqueQualityFormNameConstraint() {
IQualityFormDAO qualityFormDAO = Registry.getQualityFormDAO();
if (isNewObject()) {
return !qualityFormDAO.existsByNameAnotherTransaction(this);
} else {
try {
QualityForm c = qualityFormDAO.findUniqueByName(name);
return c.getId().equals(getId());
} catch (InstanceNotFoundException e) {
return true;
} catch (NonUniqueResultException e) {
return false;
}
}
}
@SuppressWarnings("unused")
@AssertTrue(message = "Quality form item name must be unique")
public boolean isUniqueQualityFormItemsNameConstraint() {
return (findQualityFormItemWithDuplicateName() == null);
}
@SuppressWarnings("unused")
@AssertTrue(message = "The quality form item positions must be unique and consecutive.")
public boolean isConsecutivesAndUniquesQualityFormItemPositionsConstraint() {
List<QualityFormItem> result = getListToNull(qualityFormItems);
for (QualityFormItem qualityFormItem : qualityFormItems) {
// Check if index is out of range
Integer index = qualityFormItem.getPosition();
if (index == null) {
return false;
}
if ((index.compareTo(0) < 0)
|| (index.compareTo(result.size()) >= 0)) {
return false;
}
// Check if index is repeated
if (result.get(index) != null) {
return false;
}
result.set(index, qualityFormItem);
}
// Check if the indexs are consecutives
for (QualityFormItem item : result) {
if (item == null) {
return false;
}
}
return true;
}
@SuppressWarnings("unused")
@AssertTrue(message = "The quality item positions must be correct in function to the percentage.")
public boolean isCorrectPositionsQualityFormItemsByPercentageConstraint() {
// check the position is correct in function to the percentage.
if ((qualityFormType != null)
&& (qualityFormType.equals(QualityFormType.BY_PERCENTAGE))) {
for (QualityFormItem item : qualityFormItems) {
if (item.getPosition() == null) {
return false;
}
if (!item.getPosition().equals(getCorrectPosition(item))) {
return false;
}
}
}
return true;
}
@SuppressWarnings("unused")
@AssertTrue(message = "percentages in quality form items must be unique")
public boolean isDuplicatesQualityFormItemPercentageConstraint() {
if ((qualityFormType != null)
&& (qualityFormType.equals(QualityFormType.BY_PERCENTAGE))
&& (findQualityFormItemWithDuplicatePercentage() != null)) {
return false;
}
return true;
}
@SuppressWarnings("unused")
@AssertTrue(message = "percentage should be greater than 0% and less than 100%")
public boolean isQualityFormItemsPercentageConstraint() {
if ((qualityFormItems.size() > 0) && (qualityFormType != null)
&& (qualityFormType.equals(QualityFormType.BY_ITEMS))) {
BigDecimal sum = new BigDecimal(0);
for (QualityFormItem item : qualityFormItems) {
if (item.getPercentage() == null) {
return false;
}
sum = sum.add(item.getPercentage());
}
return (sum.compareTo(new BigDecimal(100).setScale(2)) == 0);
}
return true;
}
private Integer getCorrectPosition(QualityFormItem itemToFind) {
Integer position = 0;
for (QualityFormItem item : qualityFormItems) {
if (itemToFind.getPercentage() == null) {
return null;
}
if ((((!itemToFind.equals(item)) && (item.getPercentage() != null) && (itemToFind
.getPercentage().compareTo(item.getPercentage())) > 0))
|| (item.getPercentage() == null)) {
position++;
}
}
return position;
}
private List<QualityFormItem> getListToNull(List<QualityFormItem> list) {
List<QualityFormItem> result = new ArrayList<QualityFormItem>(list
.size());
for (int i = 0; i < list.size(); i++) {
result.add(null);
}
return result;
}
private void updateAndSortQualityFormItemPositionsByPercentage() {
List<QualityFormItem> result = getListToNull(qualityFormItems);
int nulos = 0;
for (QualityFormItem item : qualityFormItems) {
Integer position = getCorrectPosition(item);
if (position == null) {
position = nulos;
nulos++;
}
while (result.get(position) != null) {
position++;
}
item.setPosition(position);
result.set(position, item);
}
setQualityFormItems(result);
}
private void updateAndSortQualityFormItemPositionsByItems() {
List<QualityFormItem> result = getListToNull(qualityFormItems);
for (QualityFormItem item : qualityFormItems) {
int position = qualityFormItems.indexOf(item);
item.setPosition(position);
result.set(position, item);
}
setQualityFormItems(result);
}
private boolean changeFromByItemsToByPercentage(
QualityFormType qualityFormType) {
return (this.qualityFormType.equals(QualityFormType.BY_ITEMS) && qualityFormType
.equals(QualityFormType.BY_PERCENTAGE));
}
private void updatePercentageByPercentage() {
BigDecimal sum = new BigDecimal(0);
for (QualityFormItem item : qualityFormItems) {
item.setPercentage(item.getPercentage().add(sum));
sum = sum.add(new BigDecimal(1));
}
}
private void updatePercentageByItems() {
if (qualityFormItems.size() > 0) {
BigDecimal percentageTotal = new BigDecimal(100).setScale(2);
BigDecimal numItems = new BigDecimal(qualityFormItems.size())
.setScale(2);
BigDecimal percentageByItem = percentageTotal.divide(numItems, 2,
BigDecimal.ROUND_DOWN);
for (QualityFormItem item : qualityFormItems) {
item.setPercentage(percentageByItem);
}
// Calculate the division remainder
BigDecimal sumByItems = (percentageByItem.multiply(numItems))
.setScale(2);
BigDecimal remainder = (percentageTotal.subtract(sumByItems))
.setScale(2);
QualityFormItem lastItem = qualityFormItems
.get(qualityFormItems.size() - 1);
BigDecimal lastPercentage = (lastItem.getPercentage()
.add(remainder)).setScale(2);
lastItem.setPercentage(lastPercentage);
}
}
private boolean checkValidPosition(Integer position) {
return (position >= 0 && position < qualityFormItems.size());
}
@NotNull(message = "report progress not specified")
public Boolean isReportAdvance() {
return BooleanUtils.toBoolean(reportAdvance);
}
public void setReportAdvance(Boolean reportAdvance) {
this.reportAdvance = BooleanUtils.toBoolean(reportAdvance);
}
public AdvanceType getAdvanceType() {
return advanceType;
}
public void setAdvanceType(AdvanceType advanceType) {
this.advanceType = advanceType;
}
@AssertTrue(message = "progress type should must be defined if quality form reports progress")
public boolean isAdvanceTypeIsNotNullIfReportAdvanceConstraint() {
if (advanceType == null) {
return !isReportAdvance();
}
return true;
}
@Override
public String getHumanId() {
return name;
}
}