Skip to content

Commit

Permalink
Properties of inherited models can be used on different child models
Browse files Browse the repository at this point in the history
  • Loading branch information
rPraml committed Oct 7, 2024
1 parent 6d5f740 commit 9d85df0
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,33 @@ public void setTenantValue(EntityBean entityBean, Object tenantId) {
setValue(entityBean, tenantId);
}

/**
* By default, getIntercept and setIntercept will check, if the passed bean is an instance of descriptor type.
* <p>
* If the property is not part of the type hierarchy (i.e. is this property from this descriptor)
* an UnsuppoertedOperation is thrown.
* <p>
* If inheritance is involved, this method returns false instead of an exception, if the property might exist in
* one of the child beans. This is necessary for getIntercept, as it returns <code>null</code> in this case.
* @return true if property can be
*/
private boolean checkPropertyAccess(EntityBean bean) {
if (bean == null || descriptor.type().isInstance(bean)) { // null = fall through - NPE is catched later.
return true;
}
InheritInfo inheritInfo = descriptor.inheritInfo();
if (inheritInfo == null || inheritInfo.isRoot() || !inheritInfo.getRoot().getType().isInstance(bean)) {
throw new IllegalArgumentException(propertyIncomatibleMsg(bean));
} else {
return false;
}
}

private String propertyIncomatibleMsg(EntityBean bean) {
String beanType = bean == null ? "null" : bean.getClass().getName();
return "Property " + name + " on [" + descriptor + "] is incompatible with type[" + beanType + "]";
}

/**
* Set the value of the property without interception or
* PropertyChangeSupport.
Expand All @@ -684,6 +711,9 @@ public void setValue(EntityBean bean, Object value) {
* Set the value of the property.
*/
public void setValueIntercept(EntityBean bean, Object value) {
if (!checkPropertyAccess(bean)) {
throw new IllegalArgumentException(propertyIncomatibleMsg(bean));
}
try {
setter.setIntercept(bean, value);
} catch (Exception ex) {
Expand Down Expand Up @@ -795,6 +825,9 @@ public Object getValue(EntityBean bean) {
}

public Object getValueIntercept(EntityBean bean) {
if (!checkPropertyAccess(bean)) {
return null;
}
try {
return getter.getIntercept(bean);
} catch (Exception ex) {
Expand Down
118 changes: 118 additions & 0 deletions ebean-test/src/test/java/org/tests/inheritance/TestPropertyAccess.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.tests.inheritance;

import io.ebean.DB;
import io.ebean.plugin.ExpressionPath;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.*;

import java.sql.Timestamp;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

/**
* @author Roland Praml, FOCONIS AG
*/
public class TestPropertyAccess {


private ExpressionPath custCretime = DB.getDefault().pluginApi().beanType(Customer.class).expressionPath("cretime");
private ExpressionPath custName = DB.getDefault().pluginApi().beanType(Customer.class).expressionPath("name");
// Note: Animal.name is only present in "Cat" not in "Dog"
private ExpressionPath animalName = DB.getDefault().pluginApi().beanType(Animal.class).expressionPath("name");
private ExpressionPath productName = DB.getDefault().pluginApi().beanType(Product.class).expressionPath("name");
private ExpressionPath animalSpecies = DB.getDefault().pluginApi().beanType(Animal.class).expressionPath("species");

@Test
void testOnInheritance() {
Cat cat = new Cat();
cat.setName("Tom");
DB.save(cat);

Dog dog = new Dog();
dog.setRegistrationNumber("FOO");
DB.save(dog);


Animal animal = DB.find(Animal.class, cat.getId());
assertThat(animalName.pathGet(animal)).isEqualTo("Tom");

animal = DB.find(Animal.class, dog.getId());
assertThat(animalName.pathGet(animal)).isNull();

animalName.pathSet(cat, "Jerry");
assertThat(cat.getName()).isEqualTo("Jerry");

assertThatThrownBy(() -> animalName.pathSet(dog, "Jerry"))
.isInstanceOf(IllegalArgumentException.class);

animalSpecies.pathSet(cat, "Angora");
animalSpecies.pathSet(dog, "Bulldog");

assertThat(cat.getSpecies()).isEqualTo("Angora");
assertThat(dog.getSpecies()).isEqualTo("Bulldog");
}

@Test
void testOnMappedSuperClass() {
Customer cust = new Customer();

Timestamp ts = new Timestamp(123);
custCretime.pathSet(cust, ts);
assertThat(custCretime.pathGet(cust)).isEqualTo(ts);

custName.pathSet(cust, "Roland");
assertThat(custName.pathGet(cust)).isEqualTo("Roland");

}

@Test
void testOnPlainBean() {
Product product = new Product();

productName.pathSet(product, "Roland");
assertThat(productName.pathGet(product)).isEqualTo("Roland");
}

@Test
void testOnContactGroup() {
ContactGroup cg = new ContactGroup();

// CHECKEM: Ist it OK to us the "custCretime" on "contactGroup"
assertThatThrownBy(() -> custCretime.pathGet(cg)).isInstanceOf(IllegalArgumentException.class);
}

@Test
void testOnCrossUsage() {
Product product = new Product();
Customer cust = new Customer();
Cat cat = new Cat();
Dog dog = new Dog();


assertThatThrownBy(() -> custCretime.pathGet(product)).isInstanceOf(IllegalArgumentException.class);
custCretime.pathGet(cust);
assertThatThrownBy(() -> custCretime.pathGet(cat)).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> custCretime.pathGet(dog)).isInstanceOf(IllegalArgumentException.class);

assertThatThrownBy(() -> custName.pathGet(product)).isInstanceOf(IllegalArgumentException.class);
custName.pathGet(cust);
assertThatThrownBy(() -> custName.pathGet(cat)).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> custName.pathGet(dog)).isInstanceOf(IllegalArgumentException.class);

assertThatThrownBy(() -> animalName.pathGet(product)).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> animalName.pathGet(cust)).isInstanceOf(IllegalArgumentException.class);
animalName.pathGet(cat);
animalName.pathGet(dog);

productName.pathGet(product);
assertThatThrownBy(() -> productName.pathGet(cust)).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> productName.pathGet(cat)).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> productName.pathGet(dog)).isInstanceOf(IllegalArgumentException.class);

assertThatThrownBy(() -> animalSpecies.pathGet(product)).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> animalSpecies.pathGet(cust)).isInstanceOf(IllegalArgumentException.class);
animalSpecies.pathGet(cat);
animalSpecies.pathGet(dog);
}
}

0 comments on commit 9d85df0

Please sign in to comment.