package org.whole.lang.tests.visitors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.whole.lang.bindings.BindingManagerFactory;
import org.whole.lang.bindings.ITransactionScope;
import org.whole.lang.commons.factories.CommonsEntityAdapterFactory;
import org.whole.lang.matchers.Matcher;
import org.whole.lang.model.IEntity;
import org.whole.lang.queries.factories.QueriesEntityFactory;
import org.whole.lang.queries.model.PathExpression;
import org.whole.lang.reflect.EntityDescriptor;
import org.whole.lang.reflect.EntityKinds;
import org.whole.lang.tests.factories.TestsEntityFactory;
import org.whole.lang.tests.model.Equals;
import org.whole.lang.tests.model.Expression;
import org.whole.lang.tests.model.FilterFamilies;
import org.whole.lang.tests.model.FilterFamily;
import org.whole.lang.tests.model.FilterRule;
import org.whole.lang.tests.model.FilterRules;
import org.whole.lang.tests.model.HasKind;
import org.whole.lang.tests.model.HasType;
import org.whole.lang.tests.model.IsAssignableTo;
import org.whole.lang.tests.model.Kind;
import org.whole.lang.tests.model.KindEnum;
import org.whole.lang.tests.model.KindEnum.Value;
import org.whole.lang.tests.model.Matches;
import org.whole.lang.tests.model.Name;
import org.whole.lang.tests.model.SubjectStatement;
import org.whole.lang.tests.model.TestSuite;
import org.whole.lang.tests.model.ThrowableType;
import org.whole.lang.tests.model.Throws;
import org.whole.lang.tests.model.UsingFilter;
import org.whole.lang.tests.reflect.TestsEntityDescriptorEnum;
import org.whole.lang.tests.util.TestsHelpers;
import org.whole.lang.util.BehaviorUtils;
import org.whole.lang.util.EntityUtils;
import org.whole.lang.util.FreshNameGenerator;
import org.whole.lang.visitors.GenericTraversalFactory;
/**
* @author Enrico Persiani
*/
public class TestsLearningInterpreterVisitor extends TestsInterpreterVisitor {
private static final String GENERATED_FAMILY_NAME = "GeneratedFilters";
private static final String GENERATED_FILTER_NAME = "generatedFilter";
protected final boolean isLearning() {
return getBindings().wIsSet("learnMode") && getBindings().wBooleanValue("learnMode");
}
protected final int learnCycles() {
return getBindings().wIsSet("learnCycles") ? getBindings().wIntValue("learnCycles") : 1;
}
@SuppressWarnings("unchecked")
protected Map<IEntity, List<IEntity>> getLearntMap() {
if (!getBindings().wIsSet("learntMap"))
getBindings().wDefValue("learntMap", new HashMap<IEntity, List<IEntity>>());
return (Map<IEntity, List<IEntity>>) getBindings().wGetValue("learntMap");
}
protected void putLearntEntity(IEntity adapter, IEntity learnt) {
List<IEntity> list = getLearntMap().get(adapter);
if (list == null)
getLearntMap().put(adapter, list = new ArrayList<IEntity>());
list.add(EntityUtils.clone(learnt));
}
protected FilterFamily getFilterFamily(TestSuite suite) {
FilterFamilies filterFamilies = suite.getFilterFamilies();
FilterFamily filterFamily = null;
for (int i=0; i<filterFamilies.wSize(); i++) {
FilterFamily child = (FilterFamily) filterFamilies.wGet(i);
if (EntityUtils.isNotResolver(child) &&
GENERATED_FAMILY_NAME.equals(child.getName().getValue())) {
filterFamily = child;
break;
}
}
TestsEntityFactory tef = TestsEntityFactory.instance;
if (filterFamily == null) {
filterFamily = tef.createFilterFamily(
CommonsEntityAdapterFactory.createResolver(
TestsEntityDescriptorEnum.PackageName),
tef.createDescription("Auto-generated filter family"),
tef.createName(GENERATED_FAMILY_NAME), tef.createFilterRules(0));
}
return filterFamily;
}
protected UsingFilter createUsingFilter(String filterName) {
TestsEntityFactory tef = TestsEntityFactory.instance;
UsingFilter usingFilter = tef.createUsingFilter();
usingFilter.setFilter(tef.createFilter(tef.createName(GENERATED_FAMILY_NAME+'.'+filterName)));
return usingFilter;
}
@Override
protected IEntity evaluate(Expression subject, boolean catchExceptions) {
IEntity entity = super.evaluate(subject, catchExceptions);
if (isLearning())
getBindings().wDef("evaluatedSubject", entity);
return entity;
}
@Override
protected void evaluate(SubjectStatement subjectStatement) {
try {
super.evaluate(subjectStatement);
} finally {
if (isLearning())
getBindings().wUnset("evaluatedSubject");
}
}
@Override
public void visit(TestSuite entity) {
if (isLearning()) {
Map<IEntity, List<IEntity>> learntMap = getLearntMap();
IEntity result = null;
for (int cycle = 1; cycle <= learnCycles(); cycle++) {
printWriter().printf("*** Learning cycle %d ***\n\n", cycle);
ITransactionScope resettableScope = BindingManagerFactory.instance.createTransactionScope();
getBindings().wEnterScope(resettableScope);
try {
getBindings().wDefValue("learnCycle", cycle);
super.visit(entity);
result = getBindings().getResult();
} finally {
resettableScope.rollback();
getBindings().wExitScope();
}
}
getBindings().setResult(result);
FilterFamily filterFamily = getFilterFamily(entity);
FilterRules filterRules = filterFamily.getFilterRules();
FreshNameGenerator fnGen = new FreshNameGenerator();
for (IEntity name : BehaviorUtils.compileAndLazyEvaluate(createFindAllFilterRuleNamesQuery(), filterFamily))
fnGen.addBoundName(name.wStringValue());
for (IEntity adapter : learntMap.keySet()) {
List<IEntity> learntEntities = learntMap.get(adapter);
IEntity value = learntEntities.get(0);
if (learntEntities.size() > 1 && EntityUtils.isData(value)) {
for (IEntity learntEntity : learntEntities)
if (!learntEntity.wEquals(value))
continue;
} else if (learntEntities.size() > 1) {
for (IEntity learntEntity2 : learntEntities)
if (EntityUtils.isData(learntEntity2))
continue;
// generate filter rule
FilterRule filterRule = TestsHelpers.createFilterRule(learntEntities);
if (EntityUtils.isNotResolver(filterRule)) {
String filterName;
IEntity filterBody;
if ((filterBody = Matcher.find(filterRule.getBody(), filterRules, false)) != null) {
// try to reuse a generated filter
filterName = ((FilterRule) filterBody.wGetParent()).getName().getValue();
} else {
// add the filter rule to the filter family
filterName = fnGen.nextFreshName(GENERATED_FILTER_NAME);
ITransactionScope resettableScope = BindingManagerFactory.instance.createTransactionScope();
getBindings().wEnterScope(resettableScope);
try {
getBindings().wDefValue("filterName", filterName);
Matcher.substitute(filterRule, getBindings(), false);
filterRules.wAdd(filterRule);
} finally {
resettableScope.rollback();
getBindings().wExitScope();
}
}
// wrap SubjectStatement with a UsingFilter
ITransactionScope resettableScope = BindingManagerFactory.instance.createTransactionScope();
getBindings().wEnterScope(resettableScope);
try {
SubjectStatement statement = BehaviorUtils.evaluateFirstResult(createFindAncestorSubjectStatement(), adapter, getBindings());
UsingFilter usingFilter = createUsingFilter(filterName);
statement.wGetParent().wSet(statement, usingFilter);
usingFilter.setSubjectStatement(statement);
} finally {
resettableScope.rollback();
getBindings().wExitScope();
}
}
}
TestsHelpers.replace(adapter, value);
}
// add the newly generated family
if (!EntityUtils.hasParent(filterFamily) && !filterRules.wIsEmpty())
entity.getFilterFamilies().wAdd(filterFamily);
} else
super.visit(entity);
}
@Override
public void visit(HasKind entity) {
Kind kind = entity.getKind();
if (isLearning() && EntityUtils.isResolver(kind)) {
EntityKinds entityKind = getBindings().wGet("evaluatedSubject").wGetEntityKind();
Value entityKindValue = KindEnum.instance.valueOf(entityKind.name());
Kind learntKind = TestsEntityFactory.instance.createKind(entityKindValue);
putLearntEntity(kind, learntKind);
setResultVisitor(GenericTraversalFactory.instance.identity());
} else
super.visit(entity);
}
@Override
public void visit(HasType entity) {
Name edName = entity.getDescriptorName();
if (isLearning() && EntityUtils.isResolver(edName)) {
EntityDescriptor<?> ed = getBindings().wGet("evaluatedSubject").wGetEntityDescriptor();
Name learntEdName = TestsEntityFactory.instance.createName(ed.getName());
putLearntEntity(edName, learntEdName);
setResultVisitor(GenericTraversalFactory.instance.identity());
} else
super.visit(entity);
}
@Override
public void visit(IsAssignableTo entity) {
Name edName = entity.getDescriptorName();
if (isLearning() && EntityUtils.isResolver(edName)) {
IEntity subject = getBindings().wGet("evaluatedSubject");
EntityDescriptor<?> ed = EntityUtils.hasParent(subject) ?
subject.wGetParent().wGetEntityDescriptor(subject) : subject.wGetEntityDescriptor();
Name learntEdName = TestsEntityFactory.instance.createName(ed.getName());
putLearntEntity(edName, learntEdName);
setResultVisitor(GenericTraversalFactory.instance.identity());
} else
super.visit(entity);
}
@Override
public void visit(Throws entity) {
ThrowableType throwableType = entity.getThrowableType();
if (isLearning() && EntityUtils.isResolver(throwableType) && getBindings().wIsSet("thrownException")) {
Exception throwable = (Exception) getBindings().wGetValue("thrownException");
getBindings().wUnset("thrownException");
ThrowableType learntThrowable = TestsEntityFactory.instance.createThrowableType(throwable.getClass().getName());
putLearntEntity(throwableType, learntThrowable);
setResultVisitor(GenericTraversalFactory.instance.identity());
} else
super.visit(entity);
}
@Override
public void visit(Equals entity) {
Expression object = entity.getObject();
if (isLearning() && EntityUtils.isResolver(object)) {
putLearntEntity(object, getBindings().wGet("evaluatedSubject"));
setResultVisitor(GenericTraversalFactory.instance.identity());
} else
super.visit(entity);
}
@Override
public void visit(Matches entity) {
Expression object = entity.getObject();
if (isLearning() && EntityUtils.isResolver(object)) {
putLearntEntity(object, getBindings().wGet("evaluatedSubject"));
setResultVisitor(GenericTraversalFactory.instance.identity());
} else
super.visit(entity);
}
private static PathExpression createFindAncestorSubjectStatement() {
QueriesEntityFactory qef = QueriesEntityFactory.instance;
return qef.createFilter(
qef.createAncestorStep(),
qef.createSubtypeTest("SubjectStatement"));
}
private static PathExpression createFindAllFilterRuleNamesQuery() {
QueriesEntityFactory qef = QueriesEntityFactory.instance;
return qef.createPath(
qef.createFeatureStep("filterRules"),
qef.createChildStep(),
qef.createFeatureStep("name"));
}
}