Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* This file is part of Codion.
*
* Codion is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Codion 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Codion. If not, see .
*
* Copyright (c) 2009 - 2024, Björn Darri Sigurðsson.
*/
package is.codion.framework.model;
import is.codion.common.db.exception.DatabaseException;
import is.codion.common.event.Event;
import is.codion.common.event.EventObserver;
import is.codion.common.state.State;
import is.codion.common.state.StateObserver;
import is.codion.common.value.AbstractValue;
import is.codion.common.value.Value;
import is.codion.framework.db.EntityConnection;
import is.codion.framework.db.EntityConnectionProvider;
import is.codion.framework.domain.entity.Entities;
import is.codion.framework.domain.entity.Entity;
import is.codion.framework.domain.entity.EntityDefinition;
import is.codion.framework.domain.entity.EntityType;
import is.codion.framework.domain.entity.EntityValidator;
import is.codion.framework.domain.entity.attribute.Attribute;
import is.codion.framework.domain.entity.attribute.AttributeDefinition;
import is.codion.framework.domain.entity.attribute.Column;
import is.codion.framework.domain.entity.attribute.ColumnDefinition;
import is.codion.framework.domain.entity.attribute.ForeignKey;
import is.codion.framework.domain.entity.attribute.ForeignKeyDefinition;
import is.codion.framework.domain.entity.attribute.TransientAttributeDefinition;
import is.codion.framework.domain.entity.exception.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static java.util.Collections.*;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toMap;
/**
* A default {@link EntityEditModel} implementation
*/
public abstract class AbstractEntityEditModel implements EntityEditModel {
private static final Logger LOG = LoggerFactory.getLogger(AbstractEntityEditModel.class);
private final Entity entity;
private final EntityConnectionProvider connectionProvider;
private final Map entitySearchModels = new HashMap<>();
private final Map, Value>> editModelValues = new ConcurrentHashMap<>();
private final Map, State> persistValues = new ConcurrentHashMap<>();
private final Map, Value>> defaultValues = new ConcurrentHashMap<>();
private final Value validator;
private final Value> modifiedPredicate;
private final Value> existsPredicate;
private final Events events = new Events();
private final States states = new States();
/**
* Instantiates a new {@link AbstractEntityEditModel} based on the given entity type.
* @param entityType the type of the entity to base this {@link AbstractEntityEditModel} on
* @param connectionProvider the {@link EntityConnectionProvider} instance
*/
protected AbstractEntityEditModel(EntityType entityType, EntityConnectionProvider connectionProvider) {
this.entity = requireNonNull(connectionProvider).entities().entity(entityType);
this.connectionProvider = connectionProvider;
this.validator = Value.builder()
.nonNull(entityDefinition().validator())
.build();
this.modifiedPredicate = Value.builder()
.nonNull((Predicate) Entity::modified)
.build();
this.existsPredicate = Value.builder()
.nonNull(entityDefinition().exists())
.build();
this.states.readOnly.set(entityDefinition().readOnly());
this.events.bindEvents();
configurePersistentForeignKeys();
setEntity(createEntity(AttributeDefinition::defaultValue));
}
@Override
public final Entities entities() {
return connectionProvider.entities();
}
@Override
public final EntityDefinition entityDefinition() {
return entity.definition();
}
@Override
public final String toString() {
return getClass() + ", " + entity.entityType();
}
@Override
public final , T> Value defaultValue(Attribute attribute) {
AttributeDefinition attributeDefinition = entityDefinition().attributes().definition(attribute);
if (defaultValues.containsKey(attribute)) {
return (Value) defaultValues.get(attribute);
}
defaultValues.put(attribute, (Value>) Value.builder()
.nonNull((S) (Supplier) attributeDefinition::defaultValue)
.build());
return (Value) defaultValues.get(attribute);
}
@Override
public final State postEditEvents() {
return states.postEditEvents;
}
@Override
public final State persist(Attribute> attribute) {
entityDefinition().attributes().definition(attribute);
return persistValues.computeIfAbsent(attribute, k -> State.state());
}
@Override
public final State readOnly() {
return states.readOnly;
}
@Override
public final State insertEnabled() {
return states.insertEnabled;
}
@Override
public final State updateEnabled() {
return states.updateEnabled;
}
@Override
public final State updateMultipleEnabled() {
return states.updateMultipleEnabled;
}
@Override
public final State deleteEnabled() {
return states.deleteEnabled;
}
@Override
public final StateObserver exists() {
return states.entityExists.observer();
}
@Override
public final StateObserver editing() {
return states.editing;
}
@Override
public final StateObserver primaryKeyNull() {
return states.primaryKeyNull.observer();
}
@Override
public final void set(Entity entity) {
events.beforeEntity.accept(entity);
setEntity(entity);
}
@Override
public final void defaults() {
setEntity(null);
}
@Override
public final EntityType entityType() {
return entity.entityType();
}
@Override
public final EntityConnectionProvider connectionProvider() {
return connectionProvider;
}
@Override
public final EntityConnection connection() {
return connectionProvider.connection();
}
@Override
public final void replace(ForeignKey foreignKey, Collection entities) {
replaceForeignKey(requireNonNull(foreignKey), requireNonNull(entities));
}
@Override
public final Entity entity() {
return entity.immutable();
}
@Override
public final StateObserver modified() {
return states.entityModified.observer();
}
@Override
public final StateObserver modified(Attribute> attribute) {
return states.modifiedObserver(attribute);
}
@Override
public final T get(Attribute attribute) {
return entity.get(attribute);
}
@Override
public final Optional optional(Attribute attribute) {
return entity.optional(attribute);
}
@Override
public final T put(Attribute attribute, T value) {
entityDefinition().attributes().definition(attribute);
Map, Object> dependingValues = dependingValues(attribute);
T previousValue = entity.put(attribute, value);
if (!Objects.equals(value, previousValue)) {
events.notifyValueEdit(attribute, value, dependingValues);
}
return previousValue;
}
@Override
public final T remove(Attribute attribute) {
entityDefinition().attributes().definition(attribute);
T value = null;
if (entity.contains(attribute)) {
Map, Object> dependingValues = dependingValues(attribute);
value = entity.remove(attribute);
events.notifyValueEdit(attribute, null, dependingValues);
}
return value;
}
@Override
public final void revert() {
entityDefinition().attributes().get().forEach(this::revert);
}
@Override
public final void revert(Attribute attribute) {
if (modified(attribute).get()) {
put(attribute, entity.original(attribute));
}
}
@Override
public final boolean nullable(Attribute> attribute) {
return validator.get().nullable(entity, attribute);
}
@Override
public final StateObserver isNull(Attribute> attribute) {
return states.nullObserver(attribute);
}
@Override
public final StateObserver isNotNull(Attribute> attribute) {
return states.nullObserver(attribute).not();
}
@Override
public final StateObserver valid() {
return states.entityValid.observer();
}
@Override
public final StateObserver valid(Attribute> attribute) {
return states.validObserver(attribute);
}
@Override
public final void validate(Attribute> attribute) throws ValidationException {
validator.get().validate(entity, attribute);
}
@Override
public final void validate() throws ValidationException {
validate(entity);
}
@Override
public final void validate(Collection entities) throws ValidationException {
for (Entity entityToValidate : requireNonNull(entities)) {
validate(entityToValidate);
}
}
@Override
public void validate(Entity entity) throws ValidationException {
if (entity.entityType().equals(entityType())) {
validator.get().validate(entity);
}
else {
entity.definition().validator().validate(entity);
}
}
@Override
public final Entity insert() throws DatabaseException, ValidationException {
return new DefaultInsert().prepare().perform().handle().iterator().next();
}
@Override
public final Collection insert(Collection entities) throws DatabaseException, ValidationException {
return new DefaultInsert(entities).prepare().perform().handle();
}
@Override
public final Entity update() throws DatabaseException, ValidationException {
return new DefaultUpdate().prepare().perform().handle().iterator().next();
}
@Override
public final Collection update(Collection entities) throws DatabaseException, ValidationException {
return new DefaultUpdate(entities).prepare().perform().handle();
}
@Override
public final Entity delete() throws DatabaseException {
return new DefaultDelete().prepare().perform().handle().iterator().next();
}
@Override
public final Collection delete(Collection entities) throws DatabaseException {
return new DefaultDelete(entities).prepare().perform().handle();
}
@Override
public final Insert createInsert() throws ValidationException {
return new DefaultInsert();
}
@Override
public final Insert createInsert(Collection entities) throws ValidationException {
return new DefaultInsert(entities);
}
@Override
public final Update createUpdate() throws ValidationException {
return new DefaultUpdate();
}
@Override
public final Update createUpdate(Collection entities) throws ValidationException {
return new DefaultUpdate(entities);
}
@Override
public final Delete createDelete() {
return new DefaultDelete();
}
@Override
public final Delete createDelete(Collection entities) {
return new DefaultDelete(entities);
}
@Override
public final void refresh() {
try {
if (states.entityExists.get()) {
set(connection().select(entity.primaryKey()));
}
}
catch (DatabaseException e) {
throw new RuntimeException(e);
}
}
@Override
public EntitySearchModel createForeignKeySearchModel(ForeignKey foreignKey) {
entityDefinition().foreignKeys().definition(foreignKey);
Collection> searchable = entities().definition(foreignKey.referencedType()).columns().searchable();
if (searchable.isEmpty()) {
throw new IllegalStateException("No searchable columns defined for entity: " + foreignKey.referencedType());
}
return EntitySearchModel.builder(foreignKey.referencedType(), connectionProvider)
.columns(searchable)
.singleSelection(true)
.build();
}
@Override
public final EntitySearchModel foreignKeySearchModel(ForeignKey foreignKey) {
entityDefinition().foreignKeys().definition(foreignKey);
synchronized (entitySearchModels) {
// can't use computeIfAbsent here, see comment in SwingEntityEditModel.foreignKeyComboBoxModel()
EntitySearchModel entitySearchModel = entitySearchModels.get(foreignKey);
if (entitySearchModel == null) {
entitySearchModel = createForeignKeySearchModel(foreignKey);
entitySearchModels.put(foreignKey, entitySearchModel);
}
return entitySearchModel;
}
}
@Override
public final Value value(Attribute attribute) {
entityDefinition().attributes().definition(attribute);
return (Value) editModelValues.computeIfAbsent(attribute, k -> new EditModelValue<>(this, attribute));
}
@Override
public final EventObserver editEvent(Attribute attribute) {
return events.editEvent(attribute);
}
@Override
public final EventObserver valueEvent(Attribute attribute) {
return events.valueEvent(attribute);
}
@Override
public final EventObserver> valueEvent() {
return events.valueChange.observer();
}
@Override
public final EventObserver entityEvent() {
return events.entity.observer();
}
@Override
public final EventObserver beforeEntityEvent() {
return events.beforeEntity.observer();
}
@Override
public final EventObserver> beforeInsertEvent() {
return events.beforeInsert.observer();
}
@Override
public final EventObserver> afterInsertEvent() {
return events.afterInsert.observer();
}
@Override
public final EventObserver