/*
* 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.common;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import javax.validation.constraints.AssertTrue;
import org.hibernate.validator.constraints.NotEmpty;
import org.libreplan.business.common.daos.IIntegrationEntityDAO;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
/**
* Base class for all entities to be sent/received to/from other applications.
* These entities have a "code" attribute, which unlike "id" is unique among the applications to be integrated
* ("id" is only unique inside "libreplan").
*
* @author Fernando Bellas Permuy <fbellas@udc.es>
*/
public abstract class IntegrationEntity extends BaseEntity {
private String code;
private Boolean codeAutogenerated = false;
@NotEmpty(message="code not specified")
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public void setCodeAutogenerated() {
this.code = generateCode();
}
/**
* This creation method must be used when we want to create an instance
* specifying a specific code (e.g. provided by the end-user or an external
* application).
*/
protected static <T extends IntegrationEntity> T create(T integrationEntity, String code) {
BaseEntity.create(integrationEntity);
integrationEntity.setCode(code);
return integrationEntity;
}
/**
* This creation method must be used when we want the creation method to
* automatically generate a code. This is a Template method which delegates
* code generation by calling on <code>generateCode()</code>.
*/
protected static <T extends IntegrationEntity> T create(T integrationEntity) {
BaseEntity.create(integrationEntity);
integrationEntity.setCode(generateCode());
return integrationEntity;
}
/**
* This method is called by <code>create(IntegrationEntity)</code>. It
* returns an unique String UUID. The current implementation is good enough
* for entities created in test classes. However, concrete classes
* interested in automatically generating descriptive identifiers when
* calling <code>create(IntegrationEntity)</code>, will probably redefine
* this method.
*/
protected static String generateCode() {
return UUID.randomUUID().toString();
}
/**
* It checks if there exists another integration entity of the same type
* with the same code. This method is a Template method that calls on
* the private method <code>findIntegrationEntityDAO</code>, which in turn
* calls on the abstract method <code>getIntegrationEntityDAO()</code>.
* Code of entity looks like: "WORK_REPORT_0001"
*/
@AssertTrue(message="code is already used")
public boolean isUniqueCodeConstraint() {
// Check if it makes sense to check the constraint
if ( !iCodeSpecified() ) {
return true;
}
/* Check the constraint. */
IIntegrationEntityDAO<? extends IntegrationEntity> integrationEntityDAO = findIntegrationEntityDAO();
if ( isNewObject() ) {
return !integrationEntityDAO.existsByCodeAnotherTransaction(code);
} else {
try {
IntegrationEntity entity = integrationEntityDAO.findByCodeAnotherTransaction(code);
return entity.getId().equals(getId());
} catch (InstanceNotFoundException e) {
return true;
}
}
}
/**
* It returns the first repeated code in the entities received as a
* parameter. If none is repeated, it returns <code>null</code>.
* Concrete entities may use this method to implement validation rules
* for detecting repeated codes in dependent entities.
*/
protected String getFirstRepeatedCode(
Set<? extends IntegrationEntity> entities) {
Set<String> codes = new HashSet<>();
for (IntegrationEntity e : entities) {
String code = e.getCode();
if ( !StringUtils.isBlank(code) ) {
if ( codes.contains(code.toLowerCase()) ) {
return code;
} else {
codes.add(code.toLowerCase());
}
}
}
return null;
}
/**
* It returns the DAO of this entity.
*/
protected abstract IIntegrationEntityDAO<? extends IntegrationEntity> getIntegrationEntityDAO();
private IIntegrationEntityDAO<? extends IntegrationEntity> findIntegrationEntityDAO() {
IIntegrationEntityDAO<? extends IntegrationEntity> integrationEntityDAO = getIntegrationEntityDAO();
if ( !integrationEntityDAO.getEntityClass().isAssignableFrom(this.getClass())) {
throw new RuntimeException(
this.getClass().getName() +
":: getIntegrationEntityDAO returns an incompatible DAO: " +
integrationEntityDAO.getClass().getName());
} else {
return integrationEntityDAO;
}
}
private boolean iCodeSpecified() {
return !StringUtils.isBlank(code);
}
public void setCodeAutogenerated(Boolean codeAutogenerated) {
this.codeAutogenerated = codeAutogenerated;
}
public Boolean isCodeAutogenerated() {
return codeAutogenerated != null && codeAutogenerated;
}
}