package org.simantics.scl.compiler.elaboration.expressions; import static org.simantics.scl.compiler.elaboration.expressions.Expressions.loc; import org.simantics.scl.compiler.elaboration.contexts.TranslationContext; import org.simantics.scl.compiler.elaboration.java.EqRelation; import org.simantics.scl.compiler.elaboration.query.QAtom; import org.simantics.scl.compiler.elaboration.query.Query; import org.simantics.scl.compiler.elaboration.relations.SCLEntityType; import org.simantics.scl.compiler.elaboration.relations.SCLEntityType.Attribute; import org.simantics.scl.compiler.elaboration.relations.SCLEntityType.AttributeBinding; import org.simantics.scl.compiler.environment.AmbiguousNameException; import org.simantics.scl.compiler.environment.Environments; import org.simantics.scl.compiler.errors.Locations; import org.simantics.scl.compiler.internal.parsing.Token; import gnu.trove.map.hash.THashMap; public class EEntityTypeAnnotation extends ASTExpression { Expression expression; Token entityTypeName; SCLEntityType entityType; Query query; // optional THashMap attributeBindingMap; public EEntityTypeAnnotation(Expression expression, Token entityTypeName, Query query) { this.expression = expression; this.entityTypeName = entityTypeName; this.query = query; } @Override public Expression resolve(TranslationContext context) { // Resolve a subexpression expression = expression.resolve(context); // Check that we are inside a query if(context.currentPreQuery == null) { context.getErrorLog().log(location, "Entity type annotations can be used only in queries."); return new EError(location); } // Resolve entity type try { entityType = Environments.getEntityType(context.getEnvironment(), entityTypeName.text); } catch (AmbiguousNameException e) { context.getErrorLog().log(location, e.getMessage()); return new EError(location); } if(entityType == null) { context.getErrorLog().log(location, "Couldn't resolve entity type " + entityTypeName.text + "."); return new EError(location); } // Rewrite the subexpression as a separate query if it is not a variable Variable base; if(expression instanceof EVariable) base = ((EVariable)expression).variable; else { base = new Variable("?" + entityTypeName.text); context.currentPreQuery.extraVariables.add(base); context.currentPreQuery.sideQueries.add(loc(expression.location, new QAtom(EqRelation.INSTANCE, new EVariable(base), expression))); expression = loc(expression.location, new EVariable(base)); } // Resolve a related query if it exists if(query != null) { EEntityTypeAnnotation oldEntityTypeAnnotation = context.currentEntityTypeAnnotation; attributeBindingMap = new THashMap(); context.currentEntityTypeAnnotation = this; query = query.resolve(context); context.currentPreQuery.sideQueries.add(query); context.currentEntityTypeAnnotation = oldEntityTypeAnnotation; AttributeBinding[] attributeBindings; if(attributeBindingMap.isEmpty()) attributeBindings = AttributeBinding.EMPTY_ARRAY; else { attributeBindings = attributeBindingMap.values().toArray(new AttributeBinding[attributeBindingMap.size()]); for(AttributeBinding binding : attributeBindings) context.currentPreQuery.extraVariables.add(binding.variable); } context.currentPreQuery.sideQueries.add(entityType.generateQuery(context, base, attributeBindings)); } else context.currentPreQuery.sideQueries.add(entityType.generateQuery(context, base, AttributeBinding.EMPTY_ARRAY)); return expression; } @Override public void setLocationDeep(long loc) { if(location == Locations.NO_LOCATION) { location = loc; expression.setLocationDeep(loc); query.setLocationDeep(loc); } } public Expression resolveAttribute(TranslationContext context, long location, String name) { AttributeBinding binding = attributeBindingMap.get(name); if(binding == null) { Attribute attribute = entityType.getAttribute(name); if(attribute == null) { context.getErrorLog().log(location, "Attribute " + name + " is not defined in entity type " + entityTypeName.text + "."); return new EError(location); } binding = new AttributeBinding(attribute, new Variable("#"+name)); attributeBindingMap.put(name, binding); } return new EVariable(binding.variable); } @Override public Expression accept(ExpressionTransformer transformer) { return transformer.transform(this); } }