/**
* ***************************************************************************
* Copyright (c) 2010 Qcadoo Limited
* Project: Qcadoo Framework
* Version: 1.4
*
* This file is part of Qcadoo.
*
* Qcadoo 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* ***************************************************************************
*/
package com.qcadoo.model.internal;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.ExpressionService;
import com.qcadoo.model.api.FieldDefinition;
import com.qcadoo.model.api.types.BelongsToType;
import com.qcadoo.model.api.types.HasManyType;
import com.qcadoo.model.api.types.ManyToManyType;
import com.qcadoo.model.api.types.TreeType;
import com.qcadoo.model.constants.VersionableConstants;
import com.qcadoo.model.internal.api.EntityService;
import com.qcadoo.model.internal.api.HibernateService;
import com.qcadoo.model.internal.api.InternalDataDefinition;
import com.qcadoo.model.internal.api.InternalFieldDefinition;
import com.qcadoo.model.internal.types.PasswordType;
import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.proxy.HibernateProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Set;
@Service
public final class EntityServiceImpl implements EntityService {
@Autowired
private HibernateService hibernateService;
@Autowired
private ExpressionService expressionService;
@Override
public Long getId(final Object databaseEntity) {
return (Long) getField(databaseEntity, FIELD_ID);
}
public Boolean getActive(final Object databaseEntity) {
return (Boolean) getField(databaseEntity, FIELD_ACTIVE);
}
public void setActive(final Object databaseEntity, final boolean active) {
setField(databaseEntity, FIELD_ACTIVE, active);
}
@Override
public void setId(final Object databaseEntity, final Long id) {
setField(databaseEntity, FIELD_ID, id);
}
@Override
public void setField(final Object databaseEntity, final FieldDefinition fieldDefinition, final Object value) {
if (!shouldAllowToSetField(fieldDefinition, value)) {
return;
}
if (fieldDefinition.getType() instanceof BelongsToType && value != null) {
Object belongsToValue = getBelongsToFieldValue(
(InternalDataDefinition) ((BelongsToType) fieldDefinition.getType()).getDataDefinition(), value);
setField(databaseEntity, fieldDefinition.getName(), belongsToValue);
} else if (fieldDefinition.getType() instanceof HasManyType) {
setField(databaseEntity, fieldDefinition.getName(), null);
} else if (fieldDefinition.getType() instanceof TreeType) {
setField(databaseEntity, fieldDefinition.getName(), null);
} else {
setField(databaseEntity, fieldDefinition.getName(), value);
}
}
private boolean shouldAllowToSetField(final FieldDefinition fieldDefinition, final Object value) {
return ((InternalFieldDefinition) fieldDefinition).isEnabled()
&& !(fieldDefinition.getType() instanceof PasswordType && value == null);
}
private Object getBelongsToFieldValue(final InternalDataDefinition dataDefinition, final Object value) {
Long id = null;
if (value instanceof Long) {
id = (Long) value;
} else if (value instanceof Entity) {
id = ((Entity) value).getId();
} else {
id = Long.parseLong(value.toString());
}
Class<?> referencedClass = dataDefinition.getClassForEntity();
return hibernateService.getCurrentSession().load(referencedClass, id);
}
@Override
public Object getField(final Object databaseEntity, final FieldDefinition fieldDefinition) {
return getField(databaseEntity, fieldDefinition, null);
}
public Object getField(final Object databaseEntity, final FieldDefinition fieldDefinition, final Entity performer) {
if (!((InternalFieldDefinition) fieldDefinition).isEnabled()) {
return null;
}
if (fieldDefinition.getType() instanceof BelongsToType) {
return getBelongsToField(databaseEntity, fieldDefinition, performer);
}
if (fieldDefinition.getType() instanceof HasManyType) {
return getHasManyField(databaseEntity, fieldDefinition);
}
if (fieldDefinition.getType() instanceof ManyToManyType) {
return getManyToManyField(databaseEntity, fieldDefinition, performer);
}
if (fieldDefinition.getType() instanceof TreeType) {
return getTreeField(databaseEntity, fieldDefinition);
}
return getPrimitiveField(databaseEntity, fieldDefinition);
}
@Override
public Entity convertToGenericEntity(final InternalDataDefinition dataDefinition, final Object databaseEntity) {
return convertToGenericEntity(dataDefinition, databaseEntity, null);
}
public Entity convertToGenericEntity(final InternalDataDefinition dataDefinition, final Object databaseEntity,
final Entity performer) {
Entity genericEntity = null;
if (databaseEntity instanceof Object[]) {
genericEntity = dataDefinition.create();
Object[] databaseArray = (Object[]) databaseEntity;
List<String> fields = new ArrayList<String>(dataDefinition.getFields().keySet());
for (int i = 0; i < fields.size(); i++) {
if (dataDefinition.getField(fields.get(i)).getType() instanceof BelongsToType) {
InternalDataDefinition referencedDataDefinition = (InternalDataDefinition) ((BelongsToType) dataDefinition
.getField(fields.get(i)).getType()).getDataDefinition();
genericEntity.setField(fields.get(i), convertToGenericEntity(referencedDataDefinition, databaseArray[i]));
} else {
genericEntity.setField(fields.get(i), databaseArray[i]);
}
}
} else if (databaseEntity.getClass().getName().startsWith("com.qcadoo.model.beans")) {
genericEntity = dataDefinition.create(getId(databaseEntity));
if (dataDefinition.isActivable()) {
genericEntity.setActive(getActive(databaseEntity));
}
for (Entry<String, FieldDefinition> fieldDefinitionEntry : dataDefinition.getFields().entrySet()) {
if (fieldDefinitionEntry.getValue().isPersistent()
&& ((InternalFieldDefinition) fieldDefinitionEntry.getValue()).isEnabled()) {
Entity currentPerformer = performer;
if (currentPerformer == null) {
currentPerformer = genericEntity;
}
genericEntity.setField(fieldDefinitionEntry.getKey(),
getField(databaseEntity, fieldDefinitionEntry.getValue(), currentPerformer));
}
}
if (dataDefinition.isPrioritizable()) {
genericEntity.setField(dataDefinition.getPriorityField().getName(),
getField(databaseEntity, dataDefinition.getPriorityField()));
}
for (Entry<String, FieldDefinition> fieldDefinitionEntry : dataDefinition.getFields().entrySet()) {
if (fieldDefinitionEntry.getValue().getExpression() != null
&& ((InternalFieldDefinition) fieldDefinitionEntry.getValue()).isEnabled()) {
genericEntity.setField(fieldDefinitionEntry.getKey(), expressionService.getValue(genericEntity,
fieldDefinitionEntry.getValue().getExpression(), Locale.ENGLISH));
}
}
dataDefinition.callViewHook(genericEntity);
} else {
genericEntity = new DefaultEntity(dataDefinition);
genericEntity.setField(dataDefinition.getFields().keySet().iterator().next(), databaseEntity);
}
return genericEntity;
}
@SuppressWarnings("unchecked")
@Override
public Object convertToDatabaseEntity(final InternalDataDefinition dataDefinition, final Entity genericEntity,
final Object existingDatabaseEntity) {
Object databaseEntity = getDatabaseEntity(dataDefinition, genericEntity, existingDatabaseEntity);
for (Entry<String, FieldDefinition> fieldDefinitionEntry : dataDefinition.getFields().entrySet()) {
FieldDefinition fieldDefinition = fieldDefinitionEntry.getValue();
if (fieldDefinition.isPersistent() && ((InternalFieldDefinition) fieldDefinition).isEnabled()) {
Object fieldValue = genericEntity.getField(fieldDefinitionEntry.getKey());
if (fieldDefinition.getType() instanceof ManyToManyType && fieldValue instanceof Iterable) {
Set<Object> innerDatabaseEntities = Sets.newHashSet();
for (Entity innerGenericEntity : (Iterable<Entity>) fieldValue) {
innerDatabaseEntities.add(getDatabaseEntity(
(InternalDataDefinition) innerGenericEntity.getDataDefinition(), innerGenericEntity, null));
}
setField(databaseEntity, fieldDefinitionEntry.getValue(), innerDatabaseEntities);
} else {
setField(databaseEntity, fieldDefinitionEntry.getValue(), fieldValue);
}
}
}
if (dataDefinition.isPrioritizable() && genericEntity.getField(dataDefinition.getPriorityField().getName()) != null) {
setField(databaseEntity, dataDefinition.getPriorityField(),
genericEntity.getField(dataDefinition.getPriorityField().getName()));
}
if (dataDefinition.isActivable()) {
setActive(databaseEntity, genericEntity.isActive());
}
return databaseEntity;
}
private Object getDatabaseEntity(final InternalDataDefinition dataDefinition, final Entity genericEntity,
final Object existingDatabaseEntity) {
Object databaseEntity = null;
if (existingDatabaseEntity == null) {
databaseEntity = dataDefinition.getInstanceForEntity();
setId(databaseEntity, genericEntity.getId());
if(dataDefinition.isVersionable()) {
setField(databaseEntity, VersionableConstants.VERSION_FIELD_NAME, genericEntity.getLongField(VersionableConstants.VERSION_FIELD_NAME));
}
} else {
databaseEntity = existingDatabaseEntity;
}
return databaseEntity;
}
private Object getPrimitiveField(final Object databaseEntity, final FieldDefinition fieldDefinition) {
return getField(databaseEntity, fieldDefinition.getName());
}
private Object getHasManyField(final Object databaseEntity, final FieldDefinition fieldDefinition) {
Long parentId = getId(databaseEntity);
HasManyType hasManyFieldType = (HasManyType) fieldDefinition.getType();
InternalDataDefinition referencedDataDefinition = (InternalDataDefinition) hasManyFieldType.getDataDefinition();
return new EntityListImpl(referencedDataDefinition, hasManyFieldType.getJoinFieldName(), parentId);
}
private Object getManyToManyField(final Object databaseEntity, final FieldDefinition fieldDefinition, final Entity performer) {
ManyToManyType manyToManyType = (ManyToManyType) fieldDefinition.getType();
InternalDataDefinition referencedDataDefinition = (InternalDataDefinition) manyToManyType.getDataDefinition();
if(manyToManyType.isLazyLoading()){
return new ProxyList(fieldDefinition, getId(databaseEntity), performer);
} else {
@SuppressWarnings("unchecked")
Set<Object> databaseEntities = (Set<Object>) getPrimitiveField(databaseEntity, fieldDefinition);
if (databaseEntities == null) {
return null;
}
List<Entity> genericEntities = Lists.newArrayList();
@SuppressWarnings("unchecked")
final Iterable<Object> fieldValues = (Iterable<Object>) getField(databaseEntity, fieldDefinition.getName());
for (Object innerDatabaseEntity : fieldValues) {
Entity innerEntity = null;
Long id = getId(innerDatabaseEntity);
if (id == null) {
innerEntity = convertToGenericEntity(referencedDataDefinition, innerDatabaseEntity);
} else {
innerEntity = new ProxyEntity(referencedDataDefinition, id);
}
genericEntities.add(innerEntity);
}
return genericEntities;
}
}
private Object getTreeField(final Object databaseEntity, final FieldDefinition fieldDefinition) {
Long parentId = getId(databaseEntity);
TreeType treeFieldType = (TreeType) fieldDefinition.getType();
InternalDataDefinition referencedDataDefinition = (InternalDataDefinition) treeFieldType.getDataDefinition();
return new EntityTreeImpl(referencedDataDefinition, treeFieldType.getJoinFieldName(), parentId);
}
private Object getBelongsToField(final Object databaseEntity, final FieldDefinition fieldDefinition, final Entity performer) {
BelongsToType belongsToFieldType = (BelongsToType) fieldDefinition.getType();
InternalDataDefinition referencedDataDefinition = (InternalDataDefinition) belongsToFieldType.getDataDefinition();
Object value = getField(databaseEntity, fieldDefinition.getName());
if (value == null) {
return null;
}
if (performer != null && referencedDataDefinition.equals(performer.getDataDefinition()) && performer.getId() != null
&& performer.getId().equals(getId(value))) {
return performer;
}
if (belongsToFieldType.isLazyLoading()) {
Long id = null;
if (value instanceof HibernateProxy) {
id = (Long) ((HibernateProxy) value).getHibernateLazyInitializer().getIdentifier();
} else {
id = getId(getField(databaseEntity, fieldDefinition.getName()));
}
if (id == null) {
return null;
}
return new ProxyEntity(referencedDataDefinition, id);
} else {
Entity currentPerformer = performer;
if (performer == null || performer.getId() == null && referencedDataDefinition.equals(performer.getDataDefinition())) {
currentPerformer = new ProxyEntity(referencedDataDefinition, getId(value));
}
return convertToGenericEntity(referencedDataDefinition, value, currentPerformer);
}
}
private void setField(final Object databaseEntity, final String fieldName, final Object value) {
try {
PropertyUtils.setProperty(databaseEntity, fieldName, value);
} catch (Exception e) {
throw new IllegalStateException("cannot set value of the property: " + databaseEntity.getClass().getSimpleName()
+ ", " + fieldName, e);
}
}
private Object getField(final Object databaseEntity, final String fieldName) {
try {
return PropertyUtils.getProperty(databaseEntity, fieldName);
} catch (Exception e) {
throw new IllegalStateException("cannot get value of the property: " + databaseEntity.getClass().getSimpleName()
+ ", " + fieldName, e);
}
}
}