/**
* ***************************************************************************
* 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.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.EntityOpResult;
import com.qcadoo.model.api.FieldDefinition;
import com.qcadoo.model.api.search.SearchCriteriaBuilder;
import com.qcadoo.model.api.search.SearchCriterion;
import com.qcadoo.model.api.search.SearchQueryBuilder;
import com.qcadoo.model.api.search.SearchResult;
import com.qcadoo.model.internal.AbstractModelXmlConverter.HooksTag;
import com.qcadoo.model.internal.api.DataAccessService;
import com.qcadoo.model.internal.api.EntityHookDefinition;
import com.qcadoo.model.internal.api.InternalDataDefinition;
import com.qcadoo.model.internal.search.SearchCriteria;
import com.qcadoo.model.internal.search.SearchCriteriaImpl;
import com.qcadoo.model.internal.search.SearchQueryImpl;
import com.qcadoo.model.internal.types.PriorityType;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.*;
import static com.google.common.base.Preconditions.*;
import static com.qcadoo.model.api.search.SearchOrders.asc;
import static com.qcadoo.model.api.search.SearchProjections.alias;
import static com.qcadoo.model.api.search.SearchProjections.rowCount;
public final class DataDefinitionImpl implements InternalDataDefinition {
private final DataAccessService dataAccessService;
private final String pluginIdentifier;
private final String name;
private String fullyQualifiedClassName;
private final Map<String, FieldDefinition> fields = new LinkedHashMap<>();
private FieldDefinition priorityField;
private final Map<String, EntityHookDefinition> hooksByMethodPath = new HashMap<>();
private final ListMultimap<HooksTag, EntityHookDefinition> entityHooks = LinkedListMultimap.create();
private boolean deletable = true;
private boolean creatable = true;
private boolean updatable = true;
private boolean enabled = true;
private boolean auditable = false;
private boolean versionable = false;
private boolean activable = false;
private String identifierExpression = "#id";
private Class<?> classForEntity;
private MasterModel masterModel;
public DataDefinitionImpl(final String pluginIdentifier, final String name, final DataAccessService dataAccessService) {
this.pluginIdentifier = pluginIdentifier;
this.name = name;
this.dataAccessService = dataAccessService;
}
@Override
public Entity get(final Long id) {
return dataAccessService.get(this, id);
}
@Override
public List<Entity> copy(final Long... id) {
return dataAccessService.copy(this, id);
}
@Override
public EntityOpResult delete(final Long... id) {
return dataAccessService.delete(this, id);
}
@Override
public Entity save(final Entity entity) {
if (!this.equals(entity.getDataDefinition())) {
throw new IllegalStateException("Incompatible types");
}
return dataAccessService.save(this, entity);
}
@Override
public Entity fastSave(final Entity entity) {
if (!this.equals(entity.getDataDefinition())) {
throw new IllegalStateException("Incompatible types");
}
return dataAccessService.fastSave(this, entity);
}
@Override
public SearchQueryBuilder find(final String queryString) {
checkArgument(queryString != null, "HQL query string must be given");
return new SearchQueryImpl(this, dataAccessService, queryString);
}
@Override
public SearchCriteriaBuilder find() {
return new SearchCriteriaImpl(this);
}
@Override
public long count(){
return count(null);
}
@Override
public long count(final SearchCriterion criterion) {
final String countAlias = "count";
SearchCriteriaBuilder scb = find();
if (criterion != null) {
scb.add(criterion);
}
scb.setProjection(alias(rowCount(), countAlias));
scb.addOrder(asc(countAlias));
Entity countProjection = scb.setMaxResults(1).uniqueResult();
return (Long) countProjection.getField(countAlias);
}
@Override
public SearchCriteriaBuilder findWithAlias(final String alias) {
return new SearchCriteriaImpl(this, alias);
}
@Override
public SearchResult find(final SearchCriteria searchCriteria) {
return dataAccessService.find(searchCriteria);
}
@Override
public void move(final Long id, final int offset) {
dataAccessService.move(this, id, offset);
}
@Override
public void moveTo(final Long id, final int position) {
dataAccessService.moveTo(this, id, position);
}
@Override
public List<Entity> activate(final Long... ids) {
if (activable) {
return dataAccessService.activate(this, ids);
} else {
return Collections.emptyList();
}
}
@Override
public List<Entity> deactivate(final Long... ids) {
if (activable) {
return dataAccessService.deactivate(this, ids);
} else {
return Collections.emptyList();
}
}
@Override
public String getName() {
return name;
}
@Override
public String getPluginIdentifier() {
return pluginIdentifier;
}
@Override
public String getFullyQualifiedClassName() {
return fullyQualifiedClassName;
}
public void setFullyQualifiedClassName(final String fullyQualifiedClassName) {
this.fullyQualifiedClassName = fullyQualifiedClassName;
this.classForEntity = loadClassForEntity();
}
@Override
public Map<String, FieldDefinition> getFields() {
return fields;
}
public void withField(final FieldDefinition field) {
fields.put(field.getName(), field);
}
@Override
public FieldDefinition getField(final String fieldName) {
if (fields.containsKey(fieldName)) {
return fields.get(fieldName);
} else if (priorityField != null && priorityField.getName().equals(fieldName)) {
return priorityField;
} else {
return null;
}
}
@Override
public EntityHookDefinition getHook(final String type, final String className, final String methodName) {
EntityHookDefinition hook = hooksByMethodPath.get(type.toUpperCase(Locale.ENGLISH) + "." + className + "." + methodName);
checkNotNull(hook, "Cannot find hook " + type.toUpperCase(Locale.ENGLISH) + "." + className + "." + methodName
+ " for dataDefinition " + this);
return hook;
}
public List<EntityHookDefinition> getValidators() {
return entityHooks.get(HooksTag.VALIDATESWITH);
}
public List<EntityHookDefinition> getViewHooks() {
return entityHooks.get(HooksTag.ONVIEW);
}
public List<EntityHookDefinition> getCopyHooks() {
return entityHooks.get(HooksTag.ONCOPY);
}
public List<EntityHookDefinition> getCreateHooks() {
return entityHooks.get(HooksTag.ONCREATE);
}
public List<EntityHookDefinition> getSaveHooks() {
return entityHooks.get(HooksTag.ONSAVE);
}
public List<EntityHookDefinition> getUpdateHooks() {
return entityHooks.get(HooksTag.ONUPDATE);
}
public List<EntityHookDefinition> getDeleteHooks() {
return entityHooks.get(HooksTag.ONDELETE);
}
public void addValidatorHook(final EntityHookDefinition validator) {
addHook(HooksTag.VALIDATESWITH, validator);
}
public void addViewHook(final EntityHookDefinition viewHook) {
addHook(HooksTag.ONVIEW, viewHook);
}
public void addCreateHook(final EntityHookDefinition createHook) {
addHook(HooksTag.ONCREATE, createHook);
}
public void addUpdateHook(final EntityHookDefinition updateHook) {
addHook(HooksTag.ONUPDATE, updateHook);
}
public void addSaveHook(final EntityHookDefinition saveHook) {
addHook(HooksTag.ONSAVE, saveHook);
}
public void addCopyHook(final EntityHookDefinition copyHook) {
addHook(HooksTag.ONCOPY, copyHook);
}
public void addDeleteHook(final EntityHookDefinition deleteHook) {
addHook(HooksTag.ONDELETE, deleteHook);
}
public void addHook(final HooksTag tag, final EntityHookDefinition hook) {
hook.initialize(this);
hooksByMethodPath.put(tag.toString() + "." + hook.getName(), hook);
entityHooks.put(tag, hook);
}
public void setIdentifierExpression(final String identifierExpression) {
this.identifierExpression = identifierExpression;
}
@Override
public String getIdentifierExpression() {
return identifierExpression;
}
@Override
public boolean callViewHook(final Entity entity) {
return callHooks(entity, getViewHooks());
}
@Override
public boolean callCreateHook(final Entity entity) {
return callHooks(entity, getCreateHooks());
}
@Override
public boolean callUpdateHook(final Entity entity) {
return entity.isValid() && callHooks(entity, getUpdateHooks());
}
@Override
public boolean callSaveHook(final Entity entity) {
return entity.isValid() && callHooks(entity, getSaveHooks());
}
@Override
public boolean callCopyHook(final Entity entity) {
return callHooks(entity, getCopyHooks());
}
@Override
public boolean callValidators(final Entity entity) {
return callHooks(entity, getValidators());
}
@Override
public boolean callDeleteHook(final Entity entity) {
return callHooks(entity, getDeleteHooks());
}
private boolean callHooks(final Entity entity, final List<EntityHookDefinition> hooksToCall) {
for (EntityHookDefinition hook : hooksToCall) {
if (hook.isEnabled() && !hook.call(entity)) {
return false;
}
}
return true;
}
@Override
public Class<?> getClassForEntity() {
return classForEntity;
}
@Override
public Object getInstanceForEntity() {
Class<?> entityClass = getClassForEntity();
try {
return entityClass.newInstance();
} catch (InstantiationException e) {
throw new IllegalStateException("cannot instantiate class: " + getFullyQualifiedClassName(), e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("cannot instantiate class: " + getFullyQualifiedClassName(), e);
}
}
@Override
public boolean isPrioritizable() {
return priorityField != null;
}
public void addPriorityField(final FieldDefinition priorityField) {
checkState(priorityField.getType() instanceof PriorityType, "priority field has wrong type");
this.priorityField = priorityField;
}
@Override
public FieldDefinition getPriorityField() {
return priorityField;
}
@Override
public boolean isDeletable() {
return deletable;
}
public void setDeletable(final boolean deletable) {
this.deletable = deletable;
}
@Override
public boolean isUpdatable() {
return updatable;
}
@Override
public boolean isAuditable() {
return auditable;
}
@Override
public boolean isInstertable() {
return creatable;
}
public void setInsertable(final boolean creatable) {
this.creatable = creatable;
}
public void setUpdatable(final boolean updatable) {
this.updatable = updatable;
}
public void setAuditable(final boolean auditable) {
this.auditable = auditable;
}
@Override
public boolean isVersionable(){
return versionable;
}
public void setVersionable(boolean versionable){
this.versionable = versionable;
}
private Class<?> loadClassForEntity() {
try {
return Thread.currentThread().getContextClassLoader().loadClass(getFullyQualifiedClassName());
} catch (ClassNotFoundException e) {
throw new IllegalStateException("cannot find mapping class for definition: " + getFullyQualifiedClassName(), e);
}
}
@Override
public Entity create() {
return new DefaultEntity(this);
}
@Override
public Entity create(final Long id) {
return new DefaultEntity(this, id);
}
@Override
public String toString() {
return getPluginIdentifier() + "." + getName();
}
@Override
public int hashCode() {
return new HashCodeBuilder(23, 41).append(name).append(pluginIdentifier).toHashCode();
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (!(obj instanceof DataDefinitionImpl)) {
return false;
}
DataDefinitionImpl other = (DataDefinitionImpl) obj;
return new EqualsBuilder().append(name, other.name).append(pluginIdentifier, other.pluginIdentifier).isEquals();
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public void enable() {
enabled = true;
}
@Override
public void disable() {
enabled = false;
}
@Override
public boolean isActivable() {
return activable;
}
public void setActivable(final boolean activable) {
this.activable = activable;
}
@Override
public MasterModel getMasterModel() {
return masterModel;
}
@Override
public void setMasterModel(MasterModel masterModel) {
this.masterModel = masterModel;
}
@Override
public Entity getMasterModelEntity(Long id) {
return dataAccessService.getMasterModelEntity(this, id);
}
@Override
public Entity tryGetMasterModelEntity(Long id) {
if(getMasterModel() == null){
return null;
}
return dataAccessService.getMasterModelEntity(this, id);
}
}