package edu.ucsd.arcum.interpreter.ast;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.transform;
import static edu.ucsd.arcum.ArcumPlugin.DEBUG;
import static edu.ucsd.arcum.exceptions.ArcumError.fatalUserError;
import static edu.ucsd.arcum.interpreter.query.ArcumDeclarationTable.SPECIAL_ANY_VARIABLE;
import static edu.ucsd.arcum.interpreter.query.EntityDataBase.CHILD_VAR_REF;
import static edu.ucsd.arcum.interpreter.query.EntityDataBase.PARENT_VAR_REF;
import static edu.ucsd.arcum.util.StringUtil.separate;
import static java.lang.String.format;
import java.util.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.dom.*;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import edu.ucsd.arcum.exceptions.ArcumError;
import edu.ucsd.arcum.exceptions.SourceLocation;
import edu.ucsd.arcum.interpreter.ast.expressions.BooleanConjunction;
import edu.ucsd.arcum.interpreter.ast.expressions.ConstraintExpression;
import edu.ucsd.arcum.interpreter.query.*;
import edu.ucsd.arcum.interpreter.satisfier.NodesWithLocations;
import edu.ucsd.arcum.interpreter.satisfier.Satisfier;
import edu.ucsd.arcum.util.Graph;
import edu.ucsd.arcum.util.ReadOnly;
import edu.ucsd.arcum.util.StringUtil;
import edu.ucsd.arcum.util.Graph.LayeredVisitor;
public class RealizationStatement implements Constrainable
{
protected final TopLevelConstruct declaration;
private SourceLocation position;
private List<TraitSignature> tuplesRealized;
private ConstraintExpression expression;
private List<ConstraintExpression> requireClauses;
private List<ErrorMessage> messages;
private ErrorMessage singletonErrorMessage = ErrorMessage.EMPTY_MESSAGE;
public RealizationStatement(TopLevelConstruct declaration, SourceLocation position) {
this.declaration = declaration;
this.position = position;
this.tuplesRealized = Lists.newArrayList();
this.expression = null;
this.requireClauses = Lists.newArrayList();
this.messages = Lists.newArrayList();
declaration.addRealizationStatement(this);
}
// makes note of the trait being realized here, but the statement is not valid
// until it has been type checked
public void addTraitSignature(TraitSignature signature) {
tuplesRealized.add(signature);
}
// checks that either: (1) only singletons are realized; or (2) only
// non-singletons are realized.
public void typeCheckAndValidate(OptionInterface optionInterface) {
TraitSignature intfSignature = null;
int numSingletons = 0;
int numTraits = 0;
for (int i = 0; i < tuplesRealized.size(); ++i) {
TraitSignature signature = tuplesRealized.get(i);
String name = signature.getName();
if (optionInterface.declaresAsAbstract(name)) {
intfSignature = optionInterface.getTraitSignature(name);
// check consistency with the interface
if (!signature.implementsSignature(intfSignature)) {
StringBuilder builder = new StringBuilder();
intfSignature.enterTraitNames(builder);
ArcumError.fatalUserError(position,
"This realization must match \'%s\'", builder);
}
signature.inheritConstraints(intfSignature);
// find out if it's singleton or not
if (intfSignature.isSingleton()) {
++numSingletons;
}
else {
++numTraits;
}
}
else {
// not realizing an abstract trait, so it must be defined locally
signature.setOptionLocal(true);
}
}
if (numSingletons > 0 && numTraits > 0) {
ArcumError.fatalUserError(getPosition(),
"The realization statement %s is invalid: cannot"
+ " realize singletons with non-singletons", this);
}
if (numTraits > 1) {
ArcumError.fatalUserError(getPosition(), "Invalid realization:"
+ " Cannot realize multiple concepts with the same expression");
}
if (numTraits == 1) {
ConstraintExpression toConjoin = intfSignature.getInterfaceConjunct();
this.expression = BooleanConjunction.conjoin(toConjoin, this.expression);
}
// TASK: call ArcumDeclarationCode.checkFormals(formals); for each formals list
// and do the same for the interface too (somewhere else)
addRequirementsToTraitConstraints();
}
protected void addRequirementsToTraitConstraints() {
if (requireClauses.size() != messages.size()) {
ArcumError.fatalError("Assert failed: Internal issue with error messages");
}
Iterator<ConstraintExpression> i = requireClauses.iterator();
Iterator<ErrorMessage> j = messages.iterator();
while (i.hasNext()) {
ConstraintExpression requirement = i.next();
ErrorMessage message = j.next();
for (TraitSignature type : tuplesRealized) {
type.addRequiresClause(requirement, message);
}
}
}
public void setBodyExpression(ConstraintExpression bodyExpr) {
this.expression = bodyExpr;
}
public List<ConstraintExpression> getRequireClauses() {
return requireClauses;
}
public List<ErrorMessage> getErrorMessages() {
return messages;
}
public void addRequiresClause(ConstraintExpression clause, ErrorMessage message) {
requireClauses.add(clause);
messages.add(message);
for (TraitSignature signature : tuplesRealized) {
// We add the require clauses to all signatures because in the tuple
// case there is only one signature anyway, and in the singleton case
// the small redundancy of checking the same condition is ok. If this
// is a problem we can mark clauses that are already checked, and thus
// skip them. Or, we can just add the clauses to only the first one
// instead of all of them. MACNEIL: Possible bug if the names aren't in
// scope during the check. They likely will be, however.
signature.addRequiresClause(clause, message);
}
}
public void addSingletonErrorMessage(ErrorMessage message) {
this.singletonErrorMessage = message;
}
@Override public String toString() {
StringBuilder buff = new StringBuilder();
buff.append("realize ");
buff.append(StringUtil.separate(Lists.transform(tuplesRealized,
new Function<TraitSignature, String>() {
@Override public String apply(TraitSignature signature) {
return signature.toSignatureOnlyString();
}
})));
buff.append(" with\n ");
buff.append(expression.toString());
buildRequiresMessageString(buff, this);
return buff.toString();
}
public static void buildRequiresMessageString(StringBuilder buff,
Constrainable constrainable)
{
List<ConstraintExpression> conditions = constrainable.getRequireClauses();
List<ErrorMessage> messages = constrainable.getErrorMessages();
if (conditions.size() == 0 && messages.size() > 0) {
checkPairConsistency(conditions, messages);
ErrorMessage message = messages.get(0);
appendNonEmptyErrorMessage(buff, message);
}
else {
checkPairConsistency(conditions, messages);
for (int i = 0; i < conditions.size(); ++i) {
ConstraintExpression requires = conditions.get(i);
ErrorMessage errorMessage = messages.get(i);
buff.append(String.format("%n require"));
appendNonEmptyErrorMessage(buff, errorMessage);
buff.append(" {");
buff.append(requires.toString());
buff.append("} ");
}
}
}
public static void checkPairConsistency(List<ConstraintExpression> conditions,
List<ErrorMessage> messages)
{
if (conditions.size() == 0 && messages.size() > 0) {
if (messages.size() != 1) {
ArcumError.fatalError("Internal error: should just have one"
+ " error message");
}
}
else if (conditions.size() != messages.size()) {
ArcumError.fatalError("Internal error: should have parity of clauses"
+ " and error messages even when error message is empty");
}
}
public static void appendNonEmptyErrorMessage(StringBuilder buff, ErrorMessage errorMessage)
{
if (errorMessage != ErrorMessage.EMPTY_MESSAGE) {
buff.append(errorMessage.toString());
buff.append(String.format("%n"));
}
}
public boolean isSingletonRealization() {
// TASK: probably this has already been typechecked, so we just return the
// status of the first
return tuplesRealized.get(0).isSingleton();
}
public boolean isLocal() {
return tuplesRealized.get(0).isOptionLocal();
}
private interface SingletonOperation
{
void apply(RealizationStatement singletonStmt) throws CoreException;
}
private static abstract class LayerAdaptor implements
Graph.LayeredVisitor<String, CoreException>
{
final List<? extends RealizationStatement> stmts;
public LayerAdaptor(List<? extends RealizationStatement> stmts) {
this.stmts = stmts;
}
@Override public void cycleFound(List<String> cycle) throws CoreException {
RealizationStatement next = stmts.iterator().next();
fatalUserError(next.getPosition(), "(Source Position of this"
+ " error message is not accurate) Cycle related to negation, forall,"
+ " or equivalence: %s", StringUtil.separate(cycle));
}
}
public static void collectivelyRealizeStatements(
final List<? extends RealizationStatement> stmts, final EntityDataBase edb,
final OptionMatchTable table) throws CoreException
{
final @ReadOnly Map<String, RealizationStatement> stmtLookup = makeStatementLookup(stmts);
final Graph<String> dependencies = makeDependencyGraph(stmts, table, stmtLookup);
Graph.LayeredVisitor<String, CoreException> realizePerLayer;
realizePerLayer = new LayerAdaptor(stmts) {
@Override public void visitLayer(List<String> layer) throws CoreException {
List<RealizationStatement> stmts = Lists.newArrayList();
for (String traitName : layer) {
// EXAMPLE: Note the subtle difference with containsKey versus
// apply(..) != null when it comes to the null key!
RealizationStatement stmt = stmtLookup.get(traitName);
if (stmt != null && !stmts.contains(stmt)) {
stmts.add(stmt);
}
}
realizeFixedPoint(edb, table, stmts);
}
};
dependencies.iterateOverTopologicalLayers(realizePerLayer);
}
private static Graph<String> makeDependencyGraph(
List<? extends RealizationStatement> stmts, OptionMatchTable table,
@ReadOnly Map<String, RealizationStatement> stmtLookup)
{
Set<String> alreadyRealized = table.getTraitsRealized();
Graph<String> g = new Graph<String>();
for (RealizationStatement stmt : stmts) {
List<TraitSignature> realized = stmt.getTuplesRealized();
List<String> dependencies = stmt.findEvaluationDependencies(stmtLookup);
for (TraitSignature tc : realized) {
if (tc.isSingleton()) {
for (String name : tc.getNamesOfDeclaredFormals()) {
g.addNode(name);
for (String dependency : dependencies) {
g.addEdge(dependency, name);
}
}
}
else {
String traitName = tc.getName();
g.addNode(traitName);
for (String dependency : dependencies) {
g.addEdge(dependency, traitName);
}
}
}
}
if (DEBUG) {
System.out.printf("%n<graph>%n%s</graph>%n", g);
}
return g;
}
// 1. All traits non-monotonically depended upon must be realized first
// 2. All traits that a singleton references must be realized first
// 3. All singletons referenced must be realized first
// 4. Exclude what is already realized, what is already predefined, and what is
// realized by this statement
// 5. Check that the any variable is not being misused
// EXAMPLE: A "read only set" is like a Predicate
private List<String> findEvaluationDependencies(
@ReadOnly Map<String, RealizationStatement> stmtLookup)
{
final Set<String> realizedByMe = getVariablesRealized();
final Set<String> predefined = getPredefinedTraitNames();
{
Set<String> builtIns = EntityDataBase.getBuiltInTraitAndPredicateNames();
predefined.addAll(builtIns);
}
Set<String> traitDependencies = expression.findAllTraitDependencies();
// (5)
if (traitDependencies.contains(SPECIAL_ANY_VARIABLE)
&& excludesUseOfAnyVariable())
{
ArcumError.fatalUserError(expression.getPosition(), "Cannot refer to"
+ " special \"%s\" variable in this context", SPECIAL_ANY_VARIABLE);
}
traitDependencies.removeAll(predefined);
traitDependencies.removeAll(realizedByMe);
traitDependencies.remove(SPECIAL_ANY_VARIABLE);
Set<String> result = Sets.newHashSet();
if (this.isSingletonRealization() || this.isLocal()) {
// (2)
result.addAll(traitDependencies);
}
else {
// (3)
// just add the singletons and locals
for (String traitDependency : traitDependencies) {
// TASK: predefines should add trait param singleton-like dudes
if (predefined.contains(traitDependency)) {
continue;
}
// TASK !!!!!!: stmtLookup should work with singleton dudes too //// !!!!
RealizationStatement dependency = stmtLookup.get(traitDependency);
if (dependency.isSingletonRealization() || dependency.isLocal()) {
result.add(traitDependency);
}
}
}
// (1)
Set<String> nonMonotonicDependencies = expression.findNonMonotonicDependencies();
result.addAll(nonMonotonicDependencies);
// (4)
result.removeAll(predefined);
result.removeAll(realizedByMe);
result.remove(SPECIAL_ANY_VARIABLE);
return Lists.newArrayList(result);
}
protected boolean excludesUseOfAnyVariable() {
return true;
}
// MACNEIL -- For these sets we should probably support overloading by appending
// a special character and then the number of parameters. Then, uses will be
// looked up based on the number of arguments. Until then, there is no name
// overloading, so we just use the names raw.
private Set<String> getPredefinedTraitNames() {
Set<String> result = Sets.newHashSet();
List<TraitSignature> tcsToAdd;
if (declaration instanceof OptionInterface) {
OptionInterface optionInterface = (OptionInterface)declaration;
tcsToAdd = optionInterface.getSubTraitConstraints();
}
else if (declaration instanceof Option) {
Option option = (Option)declaration;
OptionInterface optionInterface = option.getOptionInterface();
tcsToAdd = optionInterface.getTraitSignatures();
}
else {
tcsToAdd = Collections.emptyList();
}
for (TraitSignature tc : tcsToAdd) {
if (tc.isSingleton()) {
for (String name : tc.getNamesOfDeclaredFormals()) {
result.add(name);
}
}
else {
result.add(tc.getName());
}
}
return result;
}
private Set<String> getVariablesRealized() {
Set<String> result = Sets.newHashSet();
for (TraitSignature tupleRealized : tuplesRealized) {
List<FormalParameter> formals = tupleRealized.getFormals();
List<String> vars = transform(formals, FormalParameter.getIdentifier);
result.addAll(vars);
}
return result;
}
// EXAMPLE: Could return @ReadOnly Map<String, RealizationStatement> or
// Function<String, RealizationStatement>
private static @ReadOnly Map<String, RealizationStatement> makeStatementLookup(
List<? extends RealizationStatement> stmts)
{
Map<String, RealizationStatement> result = Maps.newHashMap();
for (RealizationStatement stmt : stmts) {
for (TraitSignature tsc : stmt.getTuplesRealized()) {
if (tsc.isSingleton()) {
for (String name : tsc.getNamesOfDeclaredFormals()) {
result.put(name, stmt);
}
}
else {
String traitName = tsc.getName();
result.put(traitName, stmt);
}
}
}
return result;
// return Functions.forMap(result);
}
// generates the sequence of singletons and other locals
public static NodesWithLocations collectivelyGenerateLocals(
List<RealizationStatement> stmts, final OptionMatchTable table,
final Option destinationOption, final EntityDataBase edb) throws CoreException
{
final NodesWithLocations result = new NodesWithLocations();
final OptionInterface optionIntf = destinationOption.getOptionInterface();
final @ReadOnly Map<String, RealizationStatement> stmtLookup = makeStatementLookup(stmts);
final Graph<String> dependencies = makeDependencyGraph(stmts, table, stmtLookup);
Graph.LayeredVisitor<String, CoreException> generatePerLayer;
generatePerLayer = new LayerAdaptor(stmts) {
@Override public void visitLayer(List<String> layer) throws CoreException {
List<RealizationStatement> stmts = Lists.newArrayList();
for (String traitName : layer) {
// EXAMPLE: Note the subtle difference with containsKey versus
// apply(..) != null when it comes to the null key!
RealizationStatement stmt = stmtLookup.get(traitName);
if (stmt != null && !stmts.contains(stmt)) {
stmts.add(stmt);
}
}
for (RealizationStatement stmt : stmts) {
NodesWithLocations generatedLocal;
if (stmt.isSingletonRealization()) {
generatedLocal = stmt.generateSingleton(table, optionIntf);
}
else if (stmt.isLocal()) {
generatedLocal = stmt.generateLocals(table);
}
else if (stmt.isStatic()) {
realizeFixedPoint(edb, table, stmts);
continue;
}
else {
continue;
}
List<TraitValue> relations = generatedLocal.getLocations();
for (TraitValue hasARelation : relations) {
List<EntityTuple> entities = hasARelation.getEntities();
for (EntityTuple tuple : entities) {
ASTNode member = (ASTNode)tuple
.lookupEntity(EntityDataBase.CHILD_VAR_REF);
Object parent = tuple
.lookupEntity(EntityDataBase.PARENT_VAR_REF);
ITypeBinding typeBinding;
if (parent instanceof ITypeBinding) {
typeBinding = (ITypeBinding)parent;
}
else {
typeBinding = edb.lookupTypeBinding((AbstractTypeDeclaration)parent);
}
ASTUtil.recordNewParent(member, typeBinding);
}
}
result.merge(generatedLocal);
}
}
};
dependencies
.iterateOverTopologicalLayers(new LayeredVisitor<String, RuntimeException>() {
@Override public void cycleFound(List<String> cycle) {
System.out.printf("Panic!!!%n");
}
@Override public void visitLayer(List<String> layer) {
System.out.printf("Layer: %s%n%n", StringUtil.separate(layer));
}
});
dependencies.iterateOverTopologicalLayers(generatePerLayer);
return result;
}
private static void singletonTopologicalOrder(
List<RealizationStatement> singletonStmts, OptionMatchTable table,
SingletonOperation op) throws CoreException
{
ArrayList<RealizationStatement> toRealize = newArrayList(singletonStmts);
// TASK -- code to re-move
// Set<String> alreadyRealized = table.getSingletonNames();
Set<String> alreadyRealized = Sets.newHashSet();
int previousSize = toRealize.size() + 1;
while (toRealize.size() != previousSize) {
previousSize = toRealize.size();
for (Iterator<RealizationStatement> i = toRealize.iterator(); i.hasNext();) {
RealizationStatement singletonStmt = i.next();
Set<String> refs = singletonStmt.expression.getArcumVariableReferences();
Set<String> realizes = singletonStmt.getNamesRealizedBy();
refs.removeAll(alreadyRealized);
if (realizes.containsAll(refs)) {
op.apply(singletonStmt);
alreadyRealized.addAll(realizes);
i.remove();
}
else {
// MACNEIL: Come back to here: When a variable is misspelled it
// will be in the refs set but not the realizes set: Is there any
// other case we get here?
System.err.printf("Internal error or not?%n");
}
}
}
if (toRealize.size() != 0) {
ArcumError.fatalError("Circular dependency among:%n<<%s>>%n", separate(
toRealize, ">>>\n<<<"));
}
}
private static boolean checkLocalsExist(EntityDataBase edb, NodesWithLocations nodes)
{
boolean localsExist = true;
for (TraitValue ownershipRelation : nodes.getLocations()) {
for (EntityTuple relation : ownershipRelation.getEntities()) {
final Object parentEntity;
final TypeDeclaration parent;
final ASTNode newNode;
parentEntity = relation.lookupEntity(PARENT_VAR_REF);
if (parentEntity instanceof ITypeBinding) {
ITypeBinding typeBinding = (ITypeBinding)parentEntity;
parent = (TypeDeclaration)edb.lookupTypeDeclaration(typeBinding);
}
else {
parent = (TypeDeclaration)parentEntity;
}
newNode = (ASTNode)relation.lookupEntity(CHILD_VAR_REF);
BodyDeclaration actualNode = findASTLocation(parent, newNode);
if (actualNode == null) {
ArcumError.userError(new SourceLocation(parent),
"Expected to find %s in %s", Entity.getDisplayString(newNode),
Entity.getDisplayString(parent));
localsExist = false;
}
}
}
return localsExist;
}
// Are we a member of the given type?
private static BodyDeclaration findASTLocation(TypeDeclaration type, ASTNode node) {
for (Object obj : type.bodyDeclarations()) {
BodyDeclaration member = (BodyDeclaration)obj;
if (Entity.compareTo(node, member) == 0) {
return member;
}
}
return null;
}
private static void realizeFixedPoint(EntityDataBase edb, OptionMatchTable table,
Collection<RealizationStatement> statements) throws CoreException
{
if (true || DEBUG) {
System.out.printf("%nFixed-pointing:%n%s%n", StringUtil.separate(statements,
format("%n")));
}
addTraits(statements, table);
checkForValidNumberOfStatements(statements);
if (statements.size() == 1) {
RealizationStatement stmt = statements.iterator().next();
if (stmt.isSingletonRealization()) {
stmt.realizeSingleton(edb, table);
return;
}
else if (stmt.isLocal()) {
NodesWithLocations locals = stmt.generateLocals(table);
if (!checkLocalsExist(edb, locals)) {
ArcumError.stop();
}
// and now fall through to let the regular realizeTrait handle it,
// given that we know it will find the matches
}
}
boolean updated = true;
while (updated) {
updated = false;
for (RealizationStatement stmt : statements) {
boolean addedNew = stmt.realizeTrait(edb, table);
if (addedNew) {
updated = true;
}
}
}
}
private static void addTraits(Collection<RealizationStatement> statements,
OptionMatchTable table)
{
for (RealizationStatement stmt : statements) {
if (!stmt.isSingletonRealization()) {
if (stmt.tuplesRealized.size() != 1) {
ArcumError.fatalError("Internal error: should have one of these");
}
TraitSignature traitSignature = stmt.tuplesRealized.get(0);
table.addTrait(traitSignature, stmt.isNested(), stmt.isStatic());
}
}
}
private static void checkForValidNumberOfStatements(
Collection<RealizationStatement> statements)
{
if (statements.size() > 1) {
// if there are more than one then they must all be non-singleton, non-local
for (RealizationStatement stmt : statements) {
if (stmt.isSingletonRealization() /*FRIDAY: check no longer needed? || stmt.isLocal()*/) {
ArcumError
.fatalError("Internal error: one singleton or local at a time");
}
}
}
}
private Set<String> getNamesRealizedBy() {
Set<String> result = new HashSet<String>();
for (TraitSignature type : tuplesRealized) {
result.addAll(type.getNamesOfDeclaredFormals());
}
return result;
}
private void realizeSingleton(EntityDataBase entityDataBase,
OptionMatchTable optionMatchTable) throws CoreException
{
Satisfier satisfier = new Satisfier(expression);
Collection<List<EntityTuple>> matches;
matches = satisfier.getMatches(tuplesRealized, entityDataBase, optionMatchTable);
List<EntityTuple> match = internalCheckAndExtractSingleton(optionMatchTable,
matches, entityDataBase);
for (EntityTuple tuple : match) {
optionMatchTable.addSingleton(tuple);
}
}
private List<EntityTuple> internalCheckAndExtractSingleton(OptionMatchTable lookup,
Collection<List<EntityTuple>> matches, EntityDataBase edb)
{
if (matches.size() != 1) {
String message = String.format("Expected one unique match,"
+ " but instead found %d matches", matches.size());
SourceLocation location = lookup.getLocation();
if (singletonErrorMessage != ErrorMessage.EMPTY_MESSAGE) {
String reason = singletonErrorMessage.getMessage(lookup);
message = String.format("%s (%s)", reason, message);
if (singletonErrorMessage.hasLocation()) {
location = singletonErrorMessage.getLocation(lookup, edb, lookup);
}
}
fatalUserError(location, message);
}
List<EntityTuple> match = matches.iterator().next();
return match;
}
public static List<EntityTuple> checkAndExtractSingleton(
OptionMatchTable optionMatchTable, Collection<List<EntityTuple>> matches)
{
// GETDONE -- the data given to this function is sometimes invalid
if (matches.size() != 1) {
fatalUserError(optionMatchTable.getLocation(),
"Expected one unique match, but instead found %d matches", matches.size());
}
List<EntityTuple> match = matches.iterator().next();
return match;
}
private NodesWithLocations generateSingleton(OptionMatchTable table,
OptionInterface optionIntf)
{
Satisfier satisfier = new Satisfier(expression);
NodesWithLocations singletons;
singletons = satisfier.generateSingletons(tuplesRealized, table, optionIntf);
for (EntityTuple singleton : singletons.getNodes()) {
table.addSingleton(singleton);
}
return singletons;
}
private NodesWithLocations generateLocals(OptionMatchTable table) {
Satisfier satisfier = new Satisfier(expression);
NodesWithLocations locals;
locals = satisfier.generateLocals(tuplesRealized, table);
return locals;
}
public EntityTuple generateEntityReplacement(String name, EntityTuple entity,
OptionMatchTable table, AST ast)
{
Satisfier satisfier = new Satisfier(expression);
EntityTuple result;
result = satisfier.generateEntityReplacement(tuplesRealized, table, entity, ast);
table.addTraitInstance(name, result);
return result;
}
// Assumes that typeCheckAndValidate has already been called. Returns true if
// any new instances are added to the table.
private boolean realizeTrait(EntityDataBase entityDataBase,
OptionMatchTable optionMatchTable) throws CoreException
{
TraitSignature traitSignature = tuplesRealized.get(0);
String name = traitSignature.getName();
Satisfier satisfier = new Satisfier(expression);
Collection<List<EntityTuple>> matches;
matches = satisfier.getMatches(tuplesRealized, entityDataBase, optionMatchTable);
boolean anyNew = false;
for (List<EntityTuple> match : matches) {
if (match.size() != 1) {
ArcumError.fatalError("Internal error: should only have one trait type");
}
boolean addedNew = optionMatchTable.addTraitInstance(name, match.get(0));
if (addedNew) {
anyNew = true;
}
}
return anyNew;
}
public boolean isStatic() {
return false;
}
protected boolean isNested() {
return false;
}
public SourceLocation getPosition() {
return position;
}
public void setPosition(SourceLocation position) {
this.position = position;
}
public List<TraitSignature> getTuplesRealized() {
return tuplesRealized;
}
public void verifyValidVariables() {
// we can't do this because the "any" variable is not constructive: we
// wouldn't know what to generate for it
Set<String> vars = expression.getArcumVariableReferences();
if (vars.contains(ArcumDeclarationTable.SPECIAL_ANY_VARIABLE)) {
ArcumError.fatalUserError(position, "Cannot use special \"_\" variable in"
+ " a non-static realization statement");
}
}
public void checkUserDefinedPredicates(List<TraitSignature> signatures) {
Set<String> varsInScope = TraitSignature.getGlobals(signatures);
for (TraitSignature signature : this.tuplesRealized) {
varsInScope.addAll(signature.getNamesOfDeclaredFormals());
}
expression.checkUserDefinedPredicates(signatures, varsInScope);
// EXAMPLE: Yet ANOTHER bug, did not descend to requireClauses and messages!
// Could their absence have been detected automatically with the visitor
// pattern?
for (ConstraintExpression requireClause : requireClauses) {
requireClause.checkUserDefinedPredicates(signatures, varsInScope);
}
for (ErrorMessage message : messages) {
message.checkUserDefinedPredicates(signatures, varsInScope);
}
}
protected ConstraintExpression getExpression() {
return expression;
}
}