/**
* ***************************************************************************
* 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.qcadoo.localization.api.utils.DateUtils;
import com.qcadoo.model.api.*;
import com.qcadoo.model.api.types.BelongsToType;
import com.qcadoo.model.api.validators.ErrorMessage;
import com.qcadoo.model.internal.api.EntityAwareCopyPerformers;
import com.qcadoo.model.internal.api.EntityAwareEqualsPerformers;
import com.qcadoo.model.internal.api.PerformerEntitiesChain;
import com.qcadoo.model.internal.api.ValueAndError;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.math.BigDecimal;
import java.util.*;
import static com.google.common.base.Preconditions.checkArgument;
import com.qcadoo.model.api.validators.GlobalMessage;
public final class DefaultEntity implements Entity, EntityAwareCopyPerformers, EntityAwareEqualsPerformers {
private Long id;
private final DataDefinition dataDefinition;
private final Map<String, Object> fields;
private final EntityMessagesHolder messagesHolder = new EntityMessagesHolderImpl();
private boolean notValidFlag = false;
private boolean active = true;
public DefaultEntity(final DataDefinition dataDefinition, final Long id, final Map<String, Object> fields) {
this.dataDefinition = dataDefinition;
this.id = id;
this.fields = fields;
}
public DefaultEntity(final DataDefinition dataDefinition, final Long id) {
this(dataDefinition, id, new HashMap<>());
}
public DefaultEntity(final DataDefinition dataDefinition) {
this(dataDefinition, null, new HashMap<>());
}
@Override
public void setId(final Long id) {
this.id = id;
}
@Override
public Long getId() {
return id;
}
@Override
public void setActive(final boolean active) {
this.active = active;
}
@Override
public void setField(final String fieldName, final Object fieldValue) {
fields.put(fieldName, fieldValue);
}
@Override
public Map<String, Object> getFields() {
return fields;
}
@Override
public void addGlobalError(final String message, final String... vars) {
messagesHolder.addGlobalError(message, vars);
}
@Override
public void addGlobalMessage(final String message, final String... vars) {
messagesHolder.addGlobalMessage(message, vars);
}
@Override
public void addGlobalMessage(String message, boolean autoClose, final boolean extraLarge, String... vars) {
messagesHolder.addGlobalMessage(message, autoClose, extraLarge, vars);
}
@Override
public void addGlobalError(String message, boolean autoClose, String... vars) {
messagesHolder.addGlobalError(message, autoClose, vars);
}
@Override
public void addGlobalError(String message, boolean autoClose, final boolean extraLarge, String... vars) {
messagesHolder.addGlobalError(message, autoClose, extraLarge, vars);
}
@Override
public void addError(final FieldDefinition fieldDefinition, final String message, final String... vars) {
messagesHolder.addError(fieldDefinition, message, vars);
}
@Override
public List<ErrorMessage> getGlobalErrors() {
return messagesHolder.getGlobalErrors();
}
@Override
public List<GlobalMessage> getGlobalMessages() {
return messagesHolder.getGlobalMessages();
}
@Override
public Map<String, ErrorMessage> getErrors() {
return messagesHolder.getErrors();
}
@Override
public ErrorMessage getError(final String fieldName) {
return messagesHolder.getError(fieldName);
}
@Override
public boolean isValid() {
return !notValidFlag && getErrors().isEmpty() && getGlobalErrors().isEmpty();
}
@Override
public boolean isFieldValid(final String fieldName) {
return messagesHolder.getError(fieldName) == null;
}
@Override
public void setNotValid() {
notValidFlag = true;
}
@Override
public int hashCode() {
HashCodeBuilder hcb = new HashCodeBuilder(23, 41).append(id).append(dataDefinition);
for (Map.Entry<String, Object> field : fields.entrySet()) {
if (field.getValue() instanceof Collection) {
continue;
}
if (field.getValue() instanceof Entity) {
Entity entity = (Entity) field.getValue();
hcb.append(field.getKey()).append(entity.getDataDefinition().getPluginIdentifier())
.append(entity.getDataDefinition().getName()).append(entity.getId());
} else {
hcb.append(field.getKey()).append(field.getValue());
}
}
return hcb.toHashCode();
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (!(obj instanceof Entity)) {
return false;
}
Entity other = (Entity) obj;
return equals(other, new PerformerEntitiesChainImpl(this));
}
@Override
public boolean flatEquals(final Entity otherEntity) {
return otherEntity != null && definitionsAndIdsAreEqual(otherEntity) && fieldsAreEquals(otherEntity, null, true);
}
@Override
public boolean equals(final Entity otherEntity, final PerformerEntitiesChain performersChain) {
return otherEntity != null
&& (otherEntity == performersChain.getLast() || definitionsAndIdsAreEqual(otherEntity)
&& fieldsAreEquals(otherEntity, performersChain, false));
}
private boolean definitionsAndIdsAreEqual(final Entity otherEntity) {
return new EqualsBuilder().append(id, otherEntity.getId()).append(dataDefinition, otherEntity.getDataDefinition())
.isEquals();
}
private boolean fieldsAreEquals(final Entity otherEntity, final PerformerEntitiesChain performersChain, final boolean flat) {
for (String fieldName : dataDefinition.getFields().keySet()) {
final Object fieldValue = fields.get(fieldName);
final Object otherFieldValue = otherEntity.getField(fieldName);
if (fieldValue == null) {
if (otherFieldValue != null) {
return false;
}
} else if (fieldValue instanceof Collection) {
continue;
} else if (fieldValue instanceof Entity) {
if (!flat && !belongsToAreEquals((Entity) fieldValue, (Entity) otherFieldValue, performersChain)) {
return false;
}
} else if (!fieldValue.equals(otherFieldValue)) {
return false;
}
}
return true;
}
private boolean belongsToAreEquals(final Entity fieldValue, final Entity otherFieldValue,
final PerformerEntitiesChain performersChain) {
boolean btResult;
if (fieldValue instanceof EntityAwareEqualsPerformers) {
final EntityAwareEqualsPerformers fieldEntityValue = (EntityAwareEqualsPerformers) fieldValue;
if (performersChain.find(fieldValue) == null) {
performersChain.append(this);
btResult = fieldEntityValue.equals(otherFieldValue, performersChain);
} else {
btResult = fieldEntityValue.flatEquals(otherFieldValue);
}
} else {
btResult = fieldValue.equals(otherFieldValue);
}
return btResult;
}
@Override
public DefaultEntity copy() {
return copy(new PerformerEntitiesChainImpl(this));
}
@Override
public DefaultEntity copy(final PerformerEntitiesChain performersChain) {
DefaultEntity entity = new DefaultEntity(dataDefinition, id);
for (Map.Entry<String, Object> field : fields.entrySet()) {
Object fieldValueCopy = null;
if (field.getValue() instanceof Entity) {
fieldValueCopy = copyFieldEntityValue(performersChain, (Entity) field.getValue());
} else {
fieldValueCopy = field.getValue();
}
entity.setField(field.getKey(), fieldValueCopy);
}
return entity;
}
private Entity copyFieldEntityValue(final PerformerEntitiesChain performersChain, final Entity fieldEntity) {
Entity fieldEntityCopy = null;
final Entity existingPerformer = performersChain.find(fieldEntity);
if (existingPerformer != null) {
fieldEntityCopy = existingPerformer;
} else if (fieldEntity instanceof EntityAwareCopyPerformers) {
performersChain.append(this);
fieldEntityCopy = ((EntityAwareCopyPerformers) fieldEntity).copy(performersChain);
} else {
fieldEntityCopy = fieldEntity.copy();
}
return fieldEntityCopy;
}
@Override
public Object getField(final String fieldName) {
return fields.get(fieldName);
}
@Override
public String getStringField(final String fieldName) {
return (String) getField(fieldName);
}
@Override
public boolean getBooleanField(final String fieldName) {
Object fieldValue = getField(fieldName);
if (fieldValue instanceof Boolean) {
return ((Boolean) fieldValue).booleanValue();
}
if (fieldValue instanceof String) {
return "1".equals(fieldValue) || Boolean.parseBoolean((String) fieldValue);
}
return false;
}
@Override
public BigDecimal getDecimalField(final String fieldName) {
final Object fieldValue = getField(fieldName);
if (fieldValue == null) {
return null;
}
if (fieldValue instanceof BigDecimal) {
return (BigDecimal) fieldValue;
}
final FieldDefinition fieldDefinition = dataDefinition.getField(fieldName);
if (fieldValue instanceof String && BigDecimal.class.equals(fieldDefinition.getType().getType())) {
if (StringUtils.isBlank((String) fieldValue)) {
return null;
}
final ValueAndError valueAndError = fieldDefinition.getType().toObject(fieldDefinition, fieldValue);
if (valueAndError.isValid()) {
return (BigDecimal) valueAndError.getValue();
}
}
throw new IllegalArgumentException("Field " + fieldName + " in " + dataDefinition.getPluginIdentifier() + '.'
+ dataDefinition.getName() + " does not contain correct BigDecimal value (current field value: " + fieldValue
+ ")");
}
@Override
public Integer getIntegerField(final String fieldName) {
final Object fieldValue = getField(fieldName);
if (fieldValue == null) {
return null;
}
if (fieldValue instanceof Integer) {
return (Integer) fieldValue;
}
final FieldDefinition fieldDefinition = dataDefinition.getField(fieldName);
if (fieldValue instanceof String && Integer.class.equals(fieldDefinition.getType().getType())) {
if (StringUtils.isBlank((String) fieldValue)) {
return null;
}
final ValueAndError valueAndError = fieldDefinition.getType().toObject(fieldDefinition, fieldValue);
if (valueAndError.isValid()) {
return (Integer) valueAndError.getValue();
}
}
throw new IllegalArgumentException("Field " + fieldName + " in " + dataDefinition.getPluginIdentifier() + '.'
+ dataDefinition.getName() + " does not contain correct Integer value (current field value: " + fieldValue + ")");
}
@Override
public Long getLongField(final String fieldName) {
final Object fieldValue = getField(fieldName);
if (fieldValue == null) {
return null;
}
if (fieldValue instanceof Long) {
return (Long)fieldValue;
}
final FieldDefinition fieldDefinition = dataDefinition.getField(fieldName);
if (fieldValue instanceof String && Long.class.equals(fieldDefinition.getType().getType())) {
if (StringUtils.isBlank((String) fieldValue)) {
return null;
}
final ValueAndError valueAndError = fieldDefinition.getType().toObject(fieldDefinition, fieldValue);
if (valueAndError.isValid()) {
return (Long) valueAndError.getValue();
}
}
throw new IllegalArgumentException("Field " + fieldName + " in " + dataDefinition.getPluginIdentifier() + '.'
+ dataDefinition.getName() + " does not contain correct Long value (current field value: " + fieldValue + ")");
}
@SuppressWarnings("unchecked")
@Override
public EntityList getHasManyField(final String fieldName) {
Object fieldValue = getField(fieldName);
if (fieldValue == null) {
return new DetachedEntityListImpl(dataDefinition, null);
}
if (fieldValue instanceof EntityList) {
return (EntityList) fieldValue;
}
if (fieldValue instanceof List<?>) {
return new DetachedEntityListImpl(dataDefinition, (List<Entity>) fieldValue);
}
throw new IllegalArgumentException("Field " + fieldName + " in " + dataDefinition.getPluginIdentifier() + '.'
+ dataDefinition.getName() + " does not contain value of type List<Entity> or EntityList");
}
@SuppressWarnings("unchecked")
@Override
public List<Entity> getManyToManyField(final String fieldName) {
if (getField(fieldName) == null) {
return Lists.newArrayList();
}
return (List<Entity>) getField(fieldName);
}
@Override
public EntityTree getTreeField(final String fieldName) {
return (EntityTree) getField(fieldName);
}
@Override
public Entity getBelongsToField(final String fieldName) {
if (getField(fieldName) == null) {
return null;
}
checkArgument(dataDefinition.getField(fieldName).getType() instanceof BelongsToType, "Field should be belongsTo type");
if (getField(fieldName) instanceof Number) {
return getProxyForBelongsToField(fieldName);
}
return (Entity) getField(fieldName);
}
private Entity getProxyForBelongsToField(final String fieldName) {
BelongsToType belongsToType = (BelongsToType) dataDefinition.getField(fieldName).getType();
Long belongsToEntityId = ((Number) getField(fieldName)).longValue();
return new ProxyEntity(belongsToType.getDataDefinition(), belongsToEntityId);
}
@Override
public boolean isActive() {
return active;
}
@Override
public DataDefinition getDataDefinition() {
return dataDefinition;
}
@Override
public String toString() {
StringBuilder entity = new StringBuilder("Entity[" + dataDefinition + "][id=" + id + ",active=" + active);
for (Map.Entry<String, Object> field : fields.entrySet()) {
entity.append(",").append(field.getKey()).append("=");
if (field.getValue() instanceof Collection) {
entity.append("#collection");
continue;
}
if (field.getValue() instanceof Entity) {
Entity belongsToEntity = (Entity) field.getValue();
entity.append("Entity[" + belongsToEntity.getDataDefinition() + "][id=" + belongsToEntity.getId() + "]");
} else {
entity.append(field.getValue());
}
}
return entity.append("]").toString();
}
@Override
public Date getDateField(final String fieldName) {
final Object fieldValue = getField(fieldName);
if (fieldValue == null) {
return null;
}
if (fieldValue instanceof Long) {
return new Date((Long) fieldValue);
}
if (fieldValue instanceof Date) {
Date fieldDateValue = (Date) fieldValue;
return new Date(fieldDateValue.getTime());
}
if (fieldValue instanceof String) {
return DateUtils.parseDate(fieldValue);
}
throw new IllegalArgumentException("Field " + fieldName + " in " + dataDefinition.getPluginIdentifier() + '.'
+ dataDefinition.getName() + " does not contain correct Date value");
}
}