/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Cyclos 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package nl.strohalm.cyclos.services.transfertypes;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.LinkedList;
import nl.strohalm.cyclos.dao.FetchDAO;
import nl.strohalm.cyclos.dao.accounts.transactions.AuthorizationLevelDAO;
import nl.strohalm.cyclos.dao.accounts.transactions.TransferTypeDAO;
import nl.strohalm.cyclos.entities.Relationship;
import nl.strohalm.cyclos.entities.accounts.transactions.AuthorizationLevel;
import nl.strohalm.cyclos.entities.accounts.transactions.AuthorizationLevel.Authorizer;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferType;
import nl.strohalm.cyclos.services.fetch.FetchServiceLocal;
import nl.strohalm.cyclos.utils.RelationshipHelper;
import nl.strohalm.cyclos.utils.validation.GeneralValidation;
import nl.strohalm.cyclos.utils.validation.ValidationError;
import nl.strohalm.cyclos.utils.validation.ValidationException;
import nl.strohalm.cyclos.utils.validation.Validator;
import org.apache.commons.collections.CollectionUtils;
/**
* Implementation for AuthorizationLevelService
* @author luis, Jefferson Magno
*/
public class AuthorizationLevelServiceImpl implements AuthorizationLevelServiceLocal {
/**
* Validates the authorization level before saving. If the authorizer of a level is an admin, checks the requirement for admin groups
*/
public class AdminGroupRequiredValidation implements GeneralValidation {
private static final long serialVersionUID = -4472441677571047937L;
@Override
public ValidationError validate(final Object object) {
final AuthorizationLevel authorizationLevel = (AuthorizationLevel) object;
if (authorizationLevel.getAuthorizer() == AuthorizationLevel.Authorizer.ADMIN && CollectionUtils.isEmpty(authorizationLevel.getAdminGroups())) {
return new ValidationError("authorizationLevel.error.adminGroupRequired");
}
return null;
}
}
/**
* Validates the authorization level before saving. The amount of the current level must be higher than the amount of the lower level and must be
* lesser than the amount of the higher level
* @author Jefferson Magno
*/
public class AmountValidation implements GeneralValidation {
private static final long serialVersionUID = -6923517450707542011L;
@Override
public ValidationError validate(final Object object) {
final AuthorizationLevel authorizationLevel = (AuthorizationLevel) object;
final TransferType transferType = authorizationLevel.getTransferType();
if (!CollectionUtils.isEmpty(transferType.getAuthorizationLevels())) {
final AuthorizationLevel lowerLevel = getLowerLevel(authorizationLevel);
final AuthorizationLevel higherLevel = getHigherLevel(authorizationLevel);
final BigDecimal amount = authorizationLevel.getAmount();
if (amount != null) {
if (lowerLevel != null && amount.compareTo(lowerLevel.getAmount()) < 0) {
return new ValidationError("authorizationLevel.error.lowerLevelAmount");
}
if (higherLevel != null && amount.compareTo(higherLevel.getAmount()) > 0) {
return new ValidationError("authorizationLevel.error.higherLevelAmount");
}
}
}
return null;
}
}
/**
* Validates the authorization level before saving. The receiver can be only the authorizer of level 1. The broker can be the authorizer of level
* 2 if the authorizer of level 1 is the receiver. The administrator can be authorizer of any levels.
* @author Jefferson Magno
*/
public class AuthorizerLevelValidation implements GeneralValidation {
private static final long serialVersionUID = 2297783229528452496L;
@Override
public ValidationError validate(final Object object) {
final AuthorizationLevel authorizationLevel = (AuthorizationLevel) object;
if (authorizationLevel.isTransient()) {
Integer level = authorizationLevel.getLevel();
if (level == null) {
level = getNewLevel(authorizationLevel);
}
final Authorizer authorizer = authorizationLevel.getAuthorizer();
if (authorizer == AuthorizationLevel.Authorizer.RECEIVER && level > 1) {
return new ValidationError("authorizationLevel.error.receiverAuthorizerLevel");
} else if (authorizer == AuthorizationLevel.Authorizer.BROKER) {
if (level == 2) {
final AuthorizationLevel lowerLevel = getLowerLevel(authorizationLevel);
if (lowerLevel.getAuthorizer() != AuthorizationLevel.Authorizer.RECEIVER) {
return new ValidationError("authorizationLevel.error.brokerLowerAuthorizerLevel");
}
}
if (level > 2) {
return new ValidationError("authorizationLevel.error.brokerAuthorizerLevel");
}
}
}
return null;
}
}
/**
* Validates the authorization level before saving. It's not allowed to insert more authorization levels than the number specified in the constant
* MAX_LEVELS of the class AuthorizationLevel
* @author Jefferson Magno
*/
public class MaxLevelValidation implements GeneralValidation {
private static final long serialVersionUID = -8200684320071292074L;
@Override
public ValidationError validate(final Object object) {
final AuthorizationLevel authorizationLevel = (AuthorizationLevel) object;
Integer level = authorizationLevel.getLevel();
if (level == null) {
level = getNewLevel(authorizationLevel);
}
if (level > AuthorizationLevel.MAX_LEVELS) {
return new ValidationError("authorizationLevel.error.maxLevel", AuthorizationLevel.MAX_LEVELS);
}
return null;
}
}
private AuthorizationLevelDAO authorizationLevelDao;
private FetchDAO fetchDao;
private FetchServiceLocal fetchService;
private TransferTypeDAO transferTypeDao;
public Validator getValidator() {
final Validator validator = new Validator("authorizationLevel");
validator.property("transferType").required();
validator.property("amount").required();
validator.property("authorizer").required();
validator.general(new MaxLevelValidation());
validator.general(new AmountValidation());
validator.general(new AuthorizerLevelValidation());
validator.general(new AdminGroupRequiredValidation());
return validator;
}
public AuthorizationLevel load(final Long id, final Relationship... fetch) {
return authorizationLevelDao.load(id, fetch);
}
@Override
public int remove(final Long... ids) {
// It's assumed that all authorization levels that are being removed belongs to the same transfer type
Long transferTypeId = null;
for (final Long id : ids) {
final AuthorizationLevel authorizationLevel = load(id, AuthorizationLevel.Relationships.TRANSFER_TYPE);
transferTypeId = authorizationLevel.getTransferType().getId();
break;
}
final int removedAuthorizationLevels = authorizationLevelDao.delete(ids);
updateRemainingLevels(transferTypeId);
return removedAuthorizationLevels;
}
@Override
public AuthorizationLevel save(final AuthorizationLevel authorizationLevel) {
validate(authorizationLevel);
if (authorizationLevel.isTransient()) {
final Integer newLevel = getNewLevel(authorizationLevel);
authorizationLevel.setLevel(newLevel);
return authorizationLevelDao.insert(authorizationLevel);
}
return authorizationLevelDao.update(authorizationLevel);
}
public void setAuthorizationLevelDao(final AuthorizationLevelDAO authorizationLevelDao) {
this.authorizationLevelDao = authorizationLevelDao;
}
public void setFetchDao(final FetchDAO fetchDao) {
this.fetchDao = fetchDao;
}
public void setFetchServiceLocal(final FetchServiceLocal fetchService) {
this.fetchService = fetchService;
}
public void setTransferTypeDao(final TransferTypeDAO transferTypeDao) {
this.transferTypeDao = transferTypeDao;
}
@Override
public void validate(final AuthorizationLevel authorizationLevel) throws ValidationException {
if (authorizationLevel.isPersistent()) {
// Authorizer is not an updatable field, so retrieve the saved value from the database
final AuthorizationLevel savedAuthorizationLevel = authorizationLevelDao.load(authorizationLevel.getId());
authorizationLevel.setAuthorizer(savedAuthorizationLevel.getAuthorizer());
}
final TransferType transferType = fetchDao.fetch(authorizationLevel.getTransferType(), TransferType.Relationships.AUTHORIZATION_LEVELS);
final Collection<AuthorizationLevel> authorizationLevels = fetchService.fetch(transferType.getAuthorizationLevels(), RelationshipHelper.nested(AuthorizationLevel.Relationships.TRANSFER_TYPE, AuthorizationLevel.Relationships.ADMIN_GROUPS));
transferType.setAuthorizationLevels(authorizationLevels);
authorizationLevel.setTransferType(transferType);
getValidator().validate(authorizationLevel);
}
/*
* Returns the higher authorization level of the transfer type or null if there isn't a higher level
*/
private AuthorizationLevel getHigherLevel(final AuthorizationLevel authorizationLevel) {
final TransferType transferType = authorizationLevel.getTransferType();
final LinkedList<AuthorizationLevel> authorizationLevels = new LinkedList<AuthorizationLevel>(transferType.getAuthorizationLevels());
if (CollectionUtils.isEmpty(authorizationLevels)) {
return null;
}
final Integer currentLevel = authorizationLevel.getLevel();
if (currentLevel == null) {
// We are inserting a new authorization level, so there isn't a higher level
return null;
} else {
// We are updating a saved authorization level
if (currentLevel == authorizationLevels.size() || authorizationLevels.size() == 1) {
// This level is already the highest or the only one
return null;
}
// List indexes start with 0
return authorizationLevels.get(currentLevel);
}
}
/*
* Returns the lower authorization level of the transfer type or null if there isn't a lower level
*/
private AuthorizationLevel getLowerLevel(final AuthorizationLevel authorizationLevel) {
final TransferType transferType = authorizationLevel.getTransferType();
final LinkedList<AuthorizationLevel> authorizationLevels = new LinkedList<AuthorizationLevel>(transferType.getAuthorizationLevels());
if (CollectionUtils.isEmpty(authorizationLevels)) {
return null;
}
final Integer currentLevel = authorizationLevel.getLevel();
if (currentLevel == null) {
// We are inserting a new authorization level, the lower level is the highest level saved
return authorizationLevels.getLast();
} else {
// We are updating a saved authorization level
if (currentLevel == 1 || authorizationLevels.size() == 1) {
return null;
}
// List indexes start with 0
return authorizationLevels.get(currentLevel - 2);
}
}
/*
* Calculates the level of a new authorization level based on the highest saved authorization level
*/
private Integer getNewLevel(final AuthorizationLevel authorizationLevel) {
final TransferType transferType = authorizationLevel.getTransferType();
final LinkedList<AuthorizationLevel> authorizationLevels = new LinkedList<AuthorizationLevel>(transferType.getAuthorizationLevels());
if (CollectionUtils.isEmpty(authorizationLevels)) {
return 1;
}
return authorizationLevels.size() + 1;
}
/*
* When authorization levels are removed, this method updates the level of the remaining authorization levels
*/
private void updateRemainingLevels(final Long transferTypeId) {
final TransferType transferType = transferTypeDao.load(transferTypeId, TransferType.Relationships.AUTHORIZATION_LEVELS);
final Collection<AuthorizationLevel> authorizationLevels = transferType.getAuthorizationLevels();
if (!CollectionUtils.isEmpty(authorizationLevels)) {
int currentLevel = 1;
for (final AuthorizationLevel authorizationLevel : authorizationLevels) {
authorizationLevel.setLevel(currentLevel);
authorizationLevelDao.update(authorizationLevel);
currentLevel++;
}
}
}
}