/** * *************************************************************************** * 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.qcadoo.model.api.DataDefinition; import com.qcadoo.model.api.Entity; import com.qcadoo.model.api.FieldDefinition; import com.qcadoo.model.api.types.*; import com.qcadoo.model.constants.VersionableConstants; import com.qcadoo.model.internal.api.InternalDataDefinition; import com.qcadoo.model.internal.api.InternalFieldDefinition; import com.qcadoo.model.internal.api.ValidationService; import com.qcadoo.model.internal.api.ValueAndError; import com.qcadoo.model.internal.types.PasswordType; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.util.Map; import java.util.Map.Entry; @Service public final class ValidationServiceImpl implements ValidationService { @Override public void validateGenericEntity(final InternalDataDefinition dataDefinition, final Entity genericEntity, final Entity existingGenericEntity) { validateEntityAgainstVersion(existingGenericEntity, genericEntity, dataDefinition); copyReadOnlyAndMissingFields(dataDefinition, genericEntity, existingGenericEntity); parseFields(dataDefinition, genericEntity); if (genericEntity.getId() == null) { dataDefinition.callCreateHook(genericEntity); parseAndValidateEntity(dataDefinition, genericEntity, existingGenericEntity); } else { parseAndValidateEntity(dataDefinition, genericEntity, existingGenericEntity); dataDefinition.callUpdateHook(genericEntity); } dataDefinition.callSaveHook(genericEntity); } private void copyReadOnlyAndMissingFields(final InternalDataDefinition dataDefinition, final Entity genericEntity, final Entity existingGenericEntity) { for (Map.Entry<String, FieldDefinition> field : dataDefinition.getFields().entrySet()) { Object value = null; if (existingGenericEntity != null) { value = existingGenericEntity.getField(field.getKey()); } if (field.getValue().getType() instanceof PasswordType) { continue; } if (field.getValue().isReadOnly()) { genericEntity.setField(field.getKey(), value); } if (!genericEntity.getFields().containsKey(field.getKey()) && genericEntity.getId() != null) { genericEntity.setField(field.getKey(), value); } } } private void parseFields(final InternalDataDefinition dataDefinition, final Entity genericEntity) { for (Entry<String, FieldDefinition> fieldDefinitionEntry : dataDefinition.getFields().entrySet()) { final InternalFieldDefinition fieldDefinition = (InternalFieldDefinition) fieldDefinitionEntry.getValue(); final FieldType fieldType = fieldDefinition.getType(); final Object fieldValue = genericEntity.getField(fieldDefinitionEntry.getKey()); Object parsedValue = null; if (fieldType instanceof BelongsToType) { parsedValue = parseBelongsToField(fieldDefinition, trimAndNullIfEmpty(fieldValue), genericEntity); } else { parsedValue = fieldValue; } genericEntity.setField(fieldDefinitionEntry.getKey(), parsedValue); } } private Object parseBelongsToField(final InternalFieldDefinition fieldDefinition, final Object value, final Entity validatedEntity) { Entity referencedEntity = null; if (value != null) { Long referencedEntityId = null; if (value instanceof String) { try { referencedEntityId = Long.valueOf((String) value); } catch (NumberFormatException e) { validatedEntity.addError(fieldDefinition, "qcadooView.validate.field.error.wrongType", value.getClass() .getSimpleName(), fieldDefinition.getType().getType().getSimpleName()); } } else if (value instanceof Long) { referencedEntityId = (Long) value; } else if (value instanceof Integer) { referencedEntityId = Long.valueOf((Integer) value); } else if (value instanceof Entity) { referencedEntityId = ((Entity) value).getId(); } else { validatedEntity.addError(fieldDefinition, "qcadooView.validate.field.error.wrongType", value.getClass() .getSimpleName(), fieldDefinition.getType().getType().getSimpleName()); } if (referencedEntityId != null) { BelongsToType belongsToFieldType = (BelongsToType) fieldDefinition.getType(); referencedEntity = belongsToFieldType.getDataDefinition().get(referencedEntityId); } } return referencedEntity; } private void parseAndValidateEntity(final InternalDataDefinition dataDefinition, final Entity genericEntity, final Entity existingGenericEntity) { for (Entry<String, FieldDefinition> fieldDefinitionEntry : dataDefinition.getFields().entrySet()) { final String fieldName = fieldDefinitionEntry.getKey(); final Object newValue = genericEntity.getField(fieldName); final Object oldValue = getOldFieldValue(existingGenericEntity, fieldName); final InternalFieldDefinition fieldDefinition = (InternalFieldDefinition) fieldDefinitionEntry.getValue(); final Object validatedFieldValue = parseAndValidateField(fieldDefinition, oldValue, newValue, genericEntity); genericEntity.setField(fieldName, validatedFieldValue); } if (genericEntity.isValid()) { dataDefinition.callValidators(genericEntity); } } private Object getOldFieldValue(final Entity existingEntityOrNull, final String fieldName) { if (existingEntityOrNull == null) { return null; } else { return existingEntityOrNull.getField(fieldName); } } private Object parseFieldValue(final InternalFieldDefinition fieldDefinition, final Object value, final Entity validatedEntity) { ValueAndError valueAndError = ValueAndError.empty(); if (value != null) { valueAndError = fieldDefinition.getType().toObject(fieldDefinition, value); if (!valueAndError.isValid()) { validatedEntity.addError(fieldDefinition, valueAndError.getMessage(), valueAndError.getArgs()); return null; } } return valueAndError.getValue(); } private Object parseAndValidateField(final InternalFieldDefinition fieldDefinition, final Object oldValue, final Object newValue, final Entity validatedEntity) { FieldType fieldType = fieldDefinition.getType(); Object parsedValue; if (fieldType instanceof HasManyType || fieldType instanceof TreeType || fieldType instanceof ManyToManyType) { parsedValue = newValue; } else { parsedValue = parseFieldValue(fieldDefinition, trimAndNullIfEmpty(newValue), validatedEntity); } if (validatedEntity.isFieldValid(fieldDefinition.getName()) && fieldDefinition.callValidators(validatedEntity, oldValue, parsedValue)) { return parsedValue; } else { return null; } } private Object trimAndNullIfEmpty(final Object value) { if (value instanceof String && !StringUtils.hasText((String) value)) { return null; } if (value instanceof String) { return ((String) value).trim(); } return value; } private void validateEntityAgainstVersion(final Entity databaseEntity, final Entity entity, final DataDefinition dataDefinition) { if(databaseEntity != null && dataDefinition.isVersionable()){ Long savedVersion = (Long) entity.getField(VersionableConstants.VERSION_FIELD_NAME); Long currentDbVersion = (Long)databaseEntity.getField(VersionableConstants.VERSION_FIELD_NAME); if(savedVersion.compareTo(currentDbVersion) != 0){ entity.addGlobalError("qcadooView.validate.global.optimisticLock",false, true); } } } }