package pt.ist.fenixframework.consistencyPredicates;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pt.ist.fenixframework.DomainMetaClass;
import pt.ist.fenixframework.DomainMetaObject;
import pt.ist.fenixframework.NoDomainMetaObjects;
import pt.ist.fenixframework.adt.bplustree.BPlusTree;
/**
* A <code>PublicConsistencyPredicate</code> is a {@link DomainConsistencyPredicate} that represents a predicate method that is
* either public or protected. It can override and be overridden by other <code>PublicConsistencyPredicates</code>.
*
* Therefore, during the initialization, the {@link DomainDependenceRecord}s of the overridden predicate (if any)
* are removed from this class downwards, and the new <code>PublicConsistencyPredicate</code> is executed
* for all instances of the declaring domain class and subclasses that do not override the predicate method.
* Likewise, on deletion, all its {@link DomainDependenceRecord}s are removed, and the overridden predicate (if any)
* is executed for all instances of the declaring domain class and subclasses that do not override the predicate method.
*
* @author João Neves - JoaoRoxoNeves@ist.utl.pt
**/
@NoDomainMetaObjects
public class PublicConsistencyPredicate extends PublicConsistencyPredicate_Base {
private static final Logger logger = LoggerFactory.getLogger(PublicConsistencyPredicate.class);
public PublicConsistencyPredicate() {
super();
}
public PublicConsistencyPredicate(Method predicateMethod, DomainMetaClass metaClass) {
super();
setPredicate(predicateMethod);
setDomainMetaClass(metaClass);
logger.info("Created a " + getClass().getSimpleName() + " for " + getPredicate());
}
@Override
public boolean isPublic() {
return true;
}
@Override
public boolean isPrivate() {
return false;
}
@Override
public boolean isFinal() {
return false;
}
@Override
public void setPublicConsistencyPredicateOverridden(PublicConsistencyPredicate publicConsistencyPredicateOverridden) {
checkFrameworkNotInitialized();
super.setPublicConsistencyPredicateOverridden(publicConsistencyPredicateOverridden);
}
@Override
public void addPublicConsistencyPredicateOverriding(PublicConsistencyPredicate publicConsistencyPredicatesOverriding) {
checkFrameworkNotInitialized();
super.addPublicConsistencyPredicateOverriding(publicConsistencyPredicatesOverriding);
}
@Override
public void removePublicConsistencyPredicateOverriding(PublicConsistencyPredicate publicConsistencyPredicatesOverriding) {
checkFrameworkNotInitialized();
super.removePublicConsistencyPredicateOverriding(publicConsistencyPredicatesOverriding);
}
/**
* Finds and initializes the {@link PublicConsistencyPredicate} that is being overridden by this predicate (if any).
* If such a predicate is found, deletes all of that predicate's {@link DomainDependenceRecord}s
* from this predicate's class downwards.
*/
@Override
public void initConsistencyPredicateOverridden() {
checkFrameworkNotInitialized();
PublicConsistencyPredicate overriddenPredicate = findOverriddenPredicate();
if (overriddenPredicate == null) {
return;
}
overriddenPredicate.removeDomainDependenceRecordsForMetaClassAndSubclasses(getDomainMetaClass());
setPublicConsistencyPredicateOverridden(overriddenPredicate);
logger.info("Initializing overridden predicate of " + getPredicate() + " to " + overriddenPredicate.getPredicate());
}
/**
* Deletes all of this predicate's {@link DomainDependenceRecord}s from the given metaClass downwards.
*/
private void removeDomainDependenceRecordsForMetaClassAndSubclasses(DomainMetaClass metaClass) {
removeDomainDependenceRecordsForExistingObjects(metaClass);
for (DomainMetaClass metaSubclass : metaClass.getDomainMetaSubclassSet()) {
removeDomainDependenceRecordsForMetaClassAndSubclasses(metaSubclass);
}
}
/**
* Deletes this predicate's {@link DomainDependenceRecord}s for the existing
* objects of the given {@link DomainMetaClass}.
*/
private void removeDomainDependenceRecordsForExistingObjects(DomainMetaClass metaClass) {
BPlusTree<DomainMetaObject> existingObjects = metaClass.getExistingDomainMetaObjects();
for (DomainMetaObject metaObject : existingObjects) {
DomainDependenceRecord dependenceRecord = metaObject.getOwnDependenceRecord(this);
if (dependenceRecord != null) {
dependenceRecord.delete();
}
}
}
/**
* Finds and updates the {@link PublicConsistencyPredicate} that is being overridden by this predicate (if any).
* Only performs changes if the current information about the overridden predicate is outdated.
*/
@Override
public void updateConsistencyPredicateOverridden() {
checkFrameworkNotInitialized();
PublicConsistencyPredicate overriddenPredicate = findOverriddenPredicate();
if (overriddenPredicate == getPublicConsistencyPredicateOverridden()) {
return;
}
setPublicConsistencyPredicateOverridden(overriddenPredicate);
logger.info("Updating overridden predicate of " + getPredicate() + " to "
+ ((overriddenPredicate == null) ? "null" : overriddenPredicate.getPredicate()));
}
/**
* Searches the consecutive superclasses of this predicate's class to find the first predicate that is being overridden.
*
* @return the <code>PublicConsistencyPredicate</code> that is being directly overridden by this predicate
*/
private PublicConsistencyPredicate findOverriddenPredicate() {
DomainMetaClass metaSuperclass = getDomainMetaClass().getDomainMetaSuperclass();
while (metaSuperclass != null) {
Method overriddenMethod = null;
try {
overriddenMethod = metaSuperclass.getDomainClass().getDeclaredMethod(getPredicate().getName());
} catch (NoSuchMethodException e) {
// No overridden method found, look in the next superclass
metaSuperclass = metaSuperclass.getDomainMetaSuperclass();
continue;
}
if (!overriddenMethod.isAnnotationPresent(ConsistencyPredicate.class)
&& !overriddenMethod.isAnnotationPresent(jvstm.cps.ConsistencyPredicate.class)) {
// Found an overridden method, but it's not a consistency predicate
return null;
}
if (Modifier.isPrivate(overriddenMethod.getModifiers())) {
// The consistency predicate at the superclass is private, so it is not being overridden.
return null;
}
return DomainConsistencyPredicate.readDomainConsistencyPredicate(overriddenMethod);
}
return null;
}
/**
* Executes this consistency predicate for all objects of the given {@link DomainMetaClass}, and objects of subclasses.
* The predicate will NOT be executed for objects of any subclass that overrides this consistency predicate.
*
* @param metaClass
* the {@link DomainMetaClass} for which to execute this predicate.
*/
@Override
public void executeConsistencyPredicateForMetaClassAndSubclasses(DomainMetaClass metaClass) {
if (metaClass == getDomainMetaClass()) {
// The metaClass is this very predicate's declaring class, so it is not a subclass yet.
executeConsistencyPredicateForExistingDomainObjects(metaClass);
} else {
try {
metaClass.getDomainClass().getDeclaredMethod(getPredicate().getName());
// If no exception was thrown, the method is being overridden from this class downwards,
// so stop and don't search in subclasses.
return;
} catch (NoSuchMethodException e) {
// The method is not being overridden here, so proceed with the execution for this subclass.
executeConsistencyPredicateForExistingDomainObjects(metaClass);
}
}
// Continue searching in subclasses for overriding predicates
for (DomainMetaClass metaSubclass : metaClass.getDomainMetaSubclassSet()) {
executeConsistencyPredicateForMetaClassAndSubclasses(metaSubclass);
}
}
/**
* Checks all the subclasses of this consistency predicate for any methods that override it.
* For each method found, checks that it has the {@link ConsistencyPredicate} annotation.
*
* @throws Error
* if this predicate is being overridden by a non-predicate method
*/
@Override
public void checkOverridingMethods(DomainMetaClass metaClass) {
if (metaClass == getDomainMetaClass()) {
// The metaClass is this very predicate's declaring class, so it is not a subclass yet.
} else {
try {
Method overridingMethod = metaClass.getDomainClass().getDeclaredMethod(getPredicate().getName());
// Check that the overriding method is a consistency predicate
if (!overridingMethod.isAnnotationPresent(ConsistencyPredicate.class)
&& !overridingMethod.isAnnotationPresent(jvstm.cps.ConsistencyPredicate.class)) {
throw new Error("The method " + overridingMethod.getDeclaringClass().getName() + "."
+ overridingMethod.getName()
+ "() overrides a consistency predicate, so it must also have the @ConsistencyPredicate annotation.");
}
// If no exception was thrown, the method is being overridden from this class downwards,
// so stop and don't search in subclasses.
return;
} catch (NoSuchMethodException e) {
// The method is not being overridden here
}
}
// Continue searching in subclasses for overriding predicates
for (DomainMetaClass metaSubclass : metaClass.getDomainMetaSubclassSet()) {
checkOverridingMethods(metaSubclass);
}
}
/**
* Deletes this <code>PublicConsistencyPredicate</code>.<br>
* A <code>PublicConsistencyPredicate</code> should be deleted when the consistency predicate method
* is removed from the code, or the containing class is removed.<br>
*
* This method is called when the predicate is being removed, and not the class.
* In this case, the previously-overridden predicate must be executed from this class downwards.
*
* @see PublicConsistencyPredicate#classDelete()
**/
@Override
public void delete() {
PublicConsistencyPredicate overriddenPredicate = getPublicConsistencyPredicateOverridden();
if (overriddenPredicate != null) {
logger.info("The deleted predicate "
+ getPredicate()
+ ((getPredicate() == null) ? " of " + getDomainMetaClass().getDomainClass() : "")
+ "was overriding the predicate: "
+ overriddenPredicate.getPredicate()
+ ((overriddenPredicate.getPredicate() == null) ? " of "
+ overriddenPredicate.getDomainMetaClass().getDomainClass() : "")
+ " which must be executed from the former class downwards.");
overriddenPredicate.executeConsistencyPredicateForMetaClassAndSubclasses(getDomainMetaClass());
for (PublicConsistencyPredicate predicatesOverriding : getPublicConsistencyPredicateOverridingSet()) {
predicatesOverriding.setPublicConsistencyPredicateOverridden(overriddenPredicate);
}
}
classDelete();
}
/**
* Deletes this <code>PublicConsistencyPredicate</code>.<br>
* A <code>PublicConsistencyPredicate</code> should be deleted when the* consistency predicate method
* is removed from the code, or the containing class is removed.<br>
*
* This method is called when the predicate's class is being removed.
* In this case, there is no need to execute the previously-overridden predicate.
*
* @see PublicConsistencyPredicate#delete()
**/
@Override
public void classDelete() {
for (PublicConsistencyPredicate predicatesOverriding : getPublicConsistencyPredicateOverridingSet()) {
removePublicConsistencyPredicateOverriding(predicatesOverriding);
}
setPublicConsistencyPredicateOverridden(null);
super.classDelete();
}
}