| name | judo-runtime:expression-syntax |
| description | Understand JUDO expression metamodel and bindings. Use when working with derived attributes, computed properties, transfer object mappings, or debugging expression resolution. |
| metadata | {"author":"BlackBelt Technology","version":"${project.version}"} |
Expression Syntax
Guide for understanding the JUDO expression metamodel, bindings, and derived attribute handling.
Expression Architecture
flowchart TB
subgraph "Expression Model"
EM["Expression Metamodel"]
TB["TransferObjectTypeBindingsCollector"]
MT["MappedTransferObjectTypeBindings"]
ET["EntityTypeExpressions"]
end
subgraph "Consumers"
QF["QueryFactory"]
AT["AttributeSelectorTranslator"]
DAO["DAO Layer"]
end
EM --> TB
TB --> MT
TB --> ET
MT --> QF
ET --> AT
AT --> DAO
Key Concepts
1. Transfer Object Bindings
Transfer objects map to entity types via bindings:
classDiagram
class TransferObject {
+name: String
+derivedPrice: Decimal
}
class Entity {
+basePrice: Decimal
+taxRate: Decimal
}
TransferObject --> Entity : mapped via binding
note for TransferObject "derivedPrice = basePrice * (1 + taxRate)"
2. Expression Types
| Type | Purpose | Example |
|---|
DataExpression | Value computation | basePrice * 1.2 |
ReferenceExpression | Navigation | order.customer |
LogicalExpression | Filters | status == 'ACTIVE' |
3. Binding Roles
| Role | Direction | Usage |
|---|
GETTER | Entity → TransferObject | Read derived values |
SETTER | TransferObject → Entity | Write mapped values |
TransferObjectTypeBindingsCollector
The main orchestrator for expression resolution:
MappedTransferObjectTypeBindings bindings =
collector.getTransferObjectGraph(transferObjectType);
DataExpression getterExpr =
bindings.getGetterAttributeExpressions().get(attribute);
Optional<LogicalExpression> filter =
Optional.ofNullable(bindings.getFilter());
MappedTransferObjectTypeBindings
Represents an expression tree node:
public class MappedTransferObjectTypeBindings {
private EClass entityType;
private Map<EAttribute, DataExpression> getterAttributeExpressions;
private Map<EAttribute, DataExpression> setterAttributeExpressions;
private Map<EReference, ReferenceExpression> getterReferenceExpressions;
private Map<EReference, MappedTransferObjectTypeBindings> references;
private LogicalExpression filter;
}
Debugging Expression Resolution
Issue 1: Missing Binding
Symptom: IllegalStateException: Attribute is not mapped
Debug:
TransferObjectTypeBindingsCollector collector = ...;
MappedTransferObjectTypeBindings bindings =
collector.getTransferObjectGraph(transferObjectType);
bindings.getGetterAttributeExpressions().forEach((attr, expr) -> {
log.debug("Attribute: {} -> Expression: {}", attr.getName(), expr);
});
Issue 2: Wrong Expression Type
Symptom: IllegalStateException: Getter binding is not a DataExpression
Check: Verify binding role matches expression type:
GETTER attributes → DataExpression
GETTER references → ReferenceExpression
Issue 3: Circular References
Protection: The collector uses processedMappedTransferObjectTypeBindings cache to prevent infinite recursion.
Expression Evaluation
Expressions are evaluated by the ExpressionEvaluator:
Set<String> variables = ExpressionEvaluator.getVariablesOfScope(
expression,
scope
);
boolean isStatic = variables.isEmpty();
Common Patterns
Derived Attribute
TransferObject.totalPrice = Entity.quantity * Entity.unitPrice
Binding:
@binding(role = GETTER, expression = "self.quantity * self.unitPrice")
Decimal totalPrice;
Computed Filter
TransferObject only shows active entities
Filter binding:
@filter(expression = "self.status == Status#ACTIVE")
See Also
agent-docs/architecture.md - Expression module architecture
/judo-runtime:custom-functions - Add custom translators