
com.xlrit.gears.engine.export.AstVisitor Maven / Gradle / Ivy
package com.xlrit.gears.engine.export;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.xlrit.gears.base.snel.SnelBaseVisitor;
import com.xlrit.gears.base.snel.SnelParser;
import com.xlrit.gears.engine.meta.BaseField;
import com.xlrit.gears.engine.meta.EntityInfo;
import com.xlrit.gears.engine.meta.MetaManager;
import com.xlrit.gears.engine.snel.Expression;
import com.xlrit.gears.engine.snel.ExpressionBuilderVisitor;
import lombok.RequiredArgsConstructor;
import org.antlr.v4.runtime.ParserRuleContext;
@RequiredArgsConstructor
class AstVisitor extends SnelBaseVisitor {
private static final Ast.AsteriskPath ASTERISK = new Ast.AsteriskPath();
private final ExpressionBuilderVisitor expressionBuilder = new ExpressionBuilderVisitor();
private final MetaManager metaManager;
private final Deque> currentType = new ArrayDeque<>();
@Override
public Ast visitStartExportSpec(SnelParser.StartExportSpecContext ctx) {
List items = ctx.exportItem().stream().map(this::visitExportItem).toList();
return new Ast.ExportDef(items);
}
@Override
public Ast.ExportItem visitExportItem(SnelParser.ExportItemContext ctx) {
String name = ctx.identifier().getText();
Ast.RootPath root = visitRootPath(ctx.rootPath());
return new Ast.ExportItem(name, root);
}
@Override
public Ast.RootPath visitRootPath(SnelParser.RootPathContext ctx) {
String name = ctx.identifier().getText();
EntityInfo> entityInfo = metaManager.getCollectionInfo(name);
if (entityInfo == null) {
throw new RuntimeException("Collection '" + name + "' not found");
}
List selections = getSubPaths(ctx.subPaths(), entityInfo);
Optional filter = getFilter(ctx);
return new Ast.RootPath(entityInfo, selections, filter);
}
@Override
public Ast.SubPath visitSubPath(SnelParser.SubPathContext ctx) {
return (Ast.SubPath) super.visitChildren(ctx);
}
@Override
public Ast.AttributePath visitAttributePath(SnelParser.AttributePathContext ctx) {
String name = ctx.identifier().getText();
BaseField field = currentType.peek().getField(name);
return new Ast.AttributePath(field);
}
@Override
public Ast.RelationPath visitRelationPath(SnelParser.RelationPathContext ctx) {
String name = ctx.identifier().getText();
BaseField field = currentType.peek().getField(name);
List selections = getSubPaths(ctx.subPaths(), field.getAssociatedEntityInfo());
Optional filter = getFilter(ctx);
return new Ast.RelationPath(field, selections, filter);
}
@Override
public Ast.AsteriskPath visitAsteriskPath(SnelParser.AsteriskPathContext ctx) {
return ASTERISK;
}
private Optional getFilter(ParserRuleContext ctx) {
if (ctx instanceof SnelParser.RootPathContext rootCtx) {
return Optional.ofNullable(rootCtx.filter()).map(f -> f.accept(expressionBuilder));
}
else if (ctx instanceof SnelParser.RelationPathContext relCtx) {
return Optional.ofNullable(relCtx.filter()).map(f -> f.accept(expressionBuilder));
}
throw new IllegalArgumentException();
}
private List getSubPaths(SnelParser.SubPathsContext ctx, EntityInfo> entityInfo) {
Objects.requireNonNull(ctx, "ctx is required");
Objects.requireNonNull(entityInfo, "entityInfo is required");
currentType.push(entityInfo);
List selections = normalizeSelections(ctx.subPath().stream().map(this::visitSubPath).toList());
currentType.pop();
return selections;
}
private List normalizeSelections(List selections) {
if (!(selections.contains(ASTERISK))) {
return selections;
}
Set explicitSelections = selections.stream().map(Ast.SubPath::name).collect(Collectors.toSet());
List additionalPaths = currentType.peek().getFields().stream()
.filter(field -> !explicitSelections.contains(field.getName()) && !field.isCalculated())
.map(this::createDefaultPath)
.toList();
return selections.stream()
.flatMap(selection -> selection == ASTERISK ? additionalPaths.stream() : Stream.of(selection))
.toList();
}
private Ast.SubPath createDefaultPath(BaseField baseField) {
return baseField.isEntityReference()
? new Ast.RelationPath(baseField, List.of(), Optional.empty()) // no explicit selection means that only the id and type are serialized
: new Ast.AttributePath(baseField);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy