Skip to content

Commit

Permalink
Added @PersistAsBaseType to support persistent base/abstract bean con…
Browse files Browse the repository at this point in the history
…tainer types
  • Loading branch information
SimonDan committed Mar 20, 2020
1 parent 7a1c7ac commit b7d384c
Show file tree
Hide file tree
Showing 39 changed files with 1,376 additions and 335 deletions.
11 changes: 10 additions & 1 deletion .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 11 additions & 8 deletions ojcms-beans/src/main/java/de/adito/ojcms/beans/OJBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,20 @@ private void _checkForDuplicateFieldsAndFireCreation()
private void _checkForDuplicateFields()
{
final Set<IField<?>> checker = new HashSet<>();
final List<IField<?>> duplicates = streamFields()
.filter(pField -> !checker.add(pField))

final List<IField<?>> duplicates = streamFields() //
.filter(pField -> !checker.add(pField)) //
.collect(Collectors.toList());

if (!duplicates.isEmpty())
throw new BeanFieldDuplicateException(duplicates);
}

@Override
public String toString()
{
return getClass().getSimpleName() + "{" + stream()
.map(Objects::toString)
return getClass().getSimpleName() + "{" + stream() //
.map(Objects::toString) //
.collect(Collectors.joining(", ")) + "}";
}

Expand All @@ -118,21 +120,22 @@ public boolean equals(Object pOther)
return true;
if (pOther == null || getClass() != pOther.getClass())
return false;

final Set<FieldValueTuple<?>> identifiers = getIdentifiers();
if (identifiers.isEmpty())
return false;

final OJBean other = (OJBean) pOther;
return identifiers.stream()
return identifiers.stream() //
.allMatch(pIdentifier -> Objects.equals(pIdentifier.getValue(), other.getValue(pIdentifier.getField())));
}

@Override
public int hashCode()
{
final Set<FieldValueTuple<?>> identifiers = getIdentifiers();
return identifiers.isEmpty() ? super.hashCode() : Objects.hash(identifiers.stream()
.map(FieldValueTuple::getValue)
.toArray(Object[]::new));
return identifiers.isEmpty() ? super.hashCode() : Objects.hash(identifiers.stream() //
.map(FieldValueTuple::getValue) //
.toArray(Object[]::new));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ public <TARGET> Optional<Function<VALUE, TARGET>> getFromConverter(Class<TARGET>
public <ANNOTATION extends Annotation> Optional<ANNOTATION> getAnnotation(Class<ANNOTATION> pType)
{
//noinspection unchecked
return annotations.stream()
.filter(pAnnotation -> pAnnotation.annotationType().equals(pType))
.findAny()
return annotations.stream() //
.filter(pAnnotation -> pAnnotation.annotationType().equals(pType)) //
.findAny() //
.map(pAnnotation -> (ANNOTATION) pAnnotation);
}

Expand All @@ -101,7 +101,7 @@ public Collection<Annotation> getAnnotations()
public <INFO> Optional<INFO> getAdditionalInformation(IAdditionalMemberInfo<INFO> pIdentifier)
{
//noinspection unchecked
return Optional.ofNullable(additionalInformation.get(pIdentifier))
return Optional.ofNullable(additionalInformation.get(pIdentifier)) //
.map(pInfo -> (INFO) pInfo);
}

Expand All @@ -110,6 +110,7 @@ public <INFO> void addAdditionalInformation(IAdditionalMemberInfo<INFO> pIdentif
{
if (!pIdentifier.getDataType().isAssignableFrom(pValue.getClass()))
throw new OJRuntimeException("Bad data type: " + pValue.getClass() + " should be from type " + pIdentifier.getDataType());

additionalInformation.put(pIdentifier, pValue);
}

Expand Down Expand Up @@ -149,18 +150,37 @@ public boolean isDetail()
return hasAnnotation(Detail.class);
}

@Override
public boolean equals(Object pOther)
{
if (this == pOther)
return true;

if (!(pOther instanceof AbstractField))
return false;

final AbstractField<?> that = (AbstractField<?>) pOther;
return Objects.equals(name, that.name);
}

@Override
public int hashCode()
{
return Objects.hash(name);
}

@Override
public String toString()
{
return getClass().getSimpleName() + "{" +
"dataType=" + dataType +
", name='" + name + '\'' +
", isPrivate=" + isPrivate +
", isOptional=" + isOptional +
", isFinal=" + isValueFinal() +
", neverNull=" + mustNeverBeNull() +
", annotations=" + annotations +
", additionalInformation=" + additionalInformation +
return getClass().getSimpleName() + "{" + //
"dataType=" + dataType + //
", name='" + name + '\'' + //
", isPrivate=" + isPrivate + //
", isOptional=" + isOptional + //
", isFinal=" + isValueFinal() + //
", neverNull=" + mustNeverBeNull() + //
", annotations=" + annotations + //
", additionalInformation=" + additionalInformation + //
'}';
}

Expand All @@ -172,7 +192,8 @@ public String toString()
* @param pFromConverter the converter that converts from the field's data type to the source's data type
* @param <SOURCE> the generic source data type
*/
protected <SOURCE> void registerConverter(Class<SOURCE> pSourceType, Function<SOURCE, VALUE> pToConverter, Function<VALUE, SOURCE> pFromConverter)
protected <SOURCE> void registerConverter(Class<SOURCE> pSourceType, Function<SOURCE, VALUE> pToConverter,
Function<VALUE, SOURCE> pFromConverter)
{
toConverters.put(pSourceType, pToConverter);
fromConverters.put(pSourceType, pFromConverter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public OJPersistenceException(String pDetailMessage, Throwable pCause)
*/
public OJPersistenceException(Class<? extends IBean> pNonPersistentBean)
{
super("The bean type '" + pNonPersistentBean.getName() + "' is not a persistent bean type! Should be annotated with @Persist!");
super("The bean type '" + pNonPersistentBean
.getName() + "' is not a persistent bean type! " + "Should be annotated with @Persist or @PersistAsBaseType!");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import java.lang.annotation.*;

/**
* Persistence annotation of the persistence framework.
* A bean annotated by this type will be persisted according to a specific {@link EPersistenceMode}.
* A bean annotated by this annotation will be persisted according to a specific {@link EPersistenceMode}.
* Per default, all beans of the annotated bean type will be stored in a persistent bean container.
*
* @author Simon Danner, 10.02.2018
Expand All @@ -15,17 +14,17 @@
public @interface Persist
{
/**
* The container id of the element, in which the bean/beans are stored.
* The id of the container in that the bean/beans are stored.
*
* @return the container id
*/
String containerId();

/**
* The mode in which the beans should be persisted.
* Default: they will be stored in containers.
* Default: they will be stored in a container.
*
* @return the persistence mode
* @return the persistence mode to use
*/
EPersistenceMode mode() default EPersistenceMode.CONTAINER;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package de.adito.ojcms.persistence;

import de.adito.ojcms.beans.IBean;

import java.lang.annotation.*;

/**
* A persistent bean container for beans annotated by this annotation will be created as base type container for multiple sub types.
* The base bean type itself can also be used if it is not abstract.
*
* @author Simon Danner, 10.02.2018
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PersistAsBaseType
{
/**
* The container id of the element, in which the bean/beans are stored.
*
* @return the container id
*/
String containerId();

/**
* All sub types the base type container is for. The base type itself can be included if it is not abstract.
* At least two types must be provided. The given types must be assignable from the base bean type.
*
* @return an array of bean sub types the base container should be for
*/
Class<? extends IBean>[] forSubTypes();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.adito.ojcms.persistence.datasource;

import de.adito.ojcms.beans.IBean;
import de.adito.ojcms.beans.literals.fields.IField;
import de.adito.ojcms.transactions.api.ITransaction;
import org.jetbrains.annotations.Nullable;
Expand All @@ -11,9 +12,10 @@
*
* @author Simon Danner, 01.01.2020
*/
abstract class AbstractBeanContent<KEY>
abstract class AbstractBeanContent<KEY, BEAN extends IBean>
{
private final KEY beanKey;
private final Class<BEAN> beanType;
private final Map<IField<?>, Object> content;
private final ITransaction transaction;

Expand All @@ -24,13 +26,22 @@ abstract class AbstractBeanContent<KEY>
* @param pTransaction the transaction this bean data is associated with
* @param pContent given initial content mapped by bean fields
*/
AbstractBeanContent(KEY pBeanKey, ITransaction pTransaction, Map<IField<?>, Object> pContent)
AbstractBeanContent(KEY pBeanKey, Class<BEAN> pBeanType, ITransaction pTransaction, Map<IField<?>, Object> pContent)
{
beanKey = Objects.requireNonNull(pBeanKey);
beanType = Objects.requireNonNull(pBeanType);
transaction = Objects.requireNonNull(pTransaction);
content = new HashMap<>(pContent);
}

/**
* The bean type the content is for.
*/
Class<BEAN> getBeanType()
{
return beanType;
}

/**
* Resolves the value for a bean field.
*
Expand Down Expand Up @@ -65,9 +76,9 @@ <VALUE> void setValue(IField<VALUE> pField, @Nullable VALUE pValue)
* Registers a bean value change at the associated transaction.
*
* @param pTransaction the current transaction
* @param pBeanKey the bean key associated with this content
* @param pKey the bean key associated with this content
* @param pChangedField the changed bean field
* @param pValue the new value
*/
abstract <VALUE> void registerValueChangeAtTransaction(ITransaction pTransaction, KEY pBeanKey, IField<VALUE> pChangedField, VALUE pValue);
abstract <VALUE> void registerValueChangeAtTransaction(ITransaction pTransaction, KEY pKey, IField<VALUE> pChangedField, VALUE pValue);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.adito.ojcms.persistence.datasource;

import de.adito.ojcms.beans.IBean;
import de.adito.ojcms.beans.literals.fields.IField;
import de.adito.ojcms.transactions.api.*;

Expand All @@ -8,33 +9,64 @@
/**
* Manages the content of a persistent bean within a container for an active {@link ITransaction}.
*
* @param <BEAN> the type of the bean in the container the content is for
* @author Simon Danner, 01.03.2020
*/
class BeanContentForContainer extends AbstractBeanContent<CurrentIndexKey>
class BeanContentForContainer<BEAN extends IBean> extends AbstractBeanContent<CurrentIndexKey, BEAN>
{
/**
* Initializes the bean content by requesting the initial content by the given key.
* The bean type this content is for will be requested from the transaction as well.
*
* @param pKey the key to identify the bean content in the container by index
* @param pTransaction the transaction this bean data is associated with
*/
BeanContentForContainer(CurrentIndexKey pKey, ITransaction pTransaction)
{
super(pKey, pTransaction, pTransaction.requestBeanDataByIndex(pKey).getData());
super(pKey, pTransaction.requestBeanTypeWithinContainer(pKey), pTransaction, pTransaction.requestBeanDataByIndex(pKey).getData());
}

/**
* Initializes the bean content by requesting the initial content by the given key.
*
* @param pKey the key to identify the bean content in the container by index
* @param pBeanType the type of the bean the content is for
* @param pTransaction the transaction this bean data is associated with
*/
BeanContentForContainer(CurrentIndexKey pKey, Class<BEAN> pBeanType, ITransaction pTransaction)
{
super(pKey, pBeanType, pTransaction, pTransaction.requestBeanDataByIndex(pKey).getData());
}

/**
* Initializes the bean content with given bean data.
* The bean type this content is for will be requested from the transaction.
*
* @param pKey the key to identify the bean content in the container by index
* @param pTransaction the transaction this bean data is associated with
* @param pContent given initial content mapped by bean fields
*/
BeanContentForContainer(CurrentIndexKey pKey, ITransaction pTransaction, Map<IField<?>, Object> pContent)
{
super(pKey, pTransaction, pContent);
super(pKey, pTransaction.requestBeanTypeWithinContainer(pKey), pTransaction, pContent);
}

/**
* Initializes the bean content with given bean data.
*
* @param pKey the key to identify the bean content in the container by index
* @param pBeanType the type of the bean the content is for
* @param pTransaction the transaction this bean data is associated with
* @param pContent given initial content mapped by bean fields
*/
BeanContentForContainer(CurrentIndexKey pKey, Class<BEAN> pBeanType, ITransaction pTransaction, Map<IField<?>, Object> pContent)
{
super(pKey, pBeanType, pTransaction, pContent);
}

@Override
<VALUE> void registerValueChangeAtTransaction(ITransaction pTransaction, CurrentIndexKey pKey, IField<VALUE> pChangedField, VALUE pNewValue)
<VALUE> void registerValueChangeAtTransaction(ITransaction pTransaction, CurrentIndexKey pKey, IField<VALUE> pChangedField,
VALUE pNewValue)
{
pTransaction.registerContainerBeanValueChange(pKey, pChangedField, pNewValue);
}
Expand Down
Loading

0 comments on commit b7d384c

Please sign in to comment.