Skip to content

Commit

Permalink
AQ language semantics impl Update progress
Browse files Browse the repository at this point in the history
  • Loading branch information
domko17 committed Dec 18, 2023
1 parent de8bf49 commit d4f0020
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 170 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,10 @@ public boolean equals(Object obj) {
return true;
}

if (!(obj instanceof AxiomQueryError)) {
if (!(obj instanceof AxiomQueryError axiomQueryError)) {
return false;
}

AxiomQueryError axiomQueryError = (AxiomQueryError) obj;

if (axiomQueryError.recognizer == recognizer &&
axiomQueryError.offendingSymbol == offendingSymbol &&
axiomQueryError.line == line &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.evolveum.axiom.lang.antlr;

import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CodePointCharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.*;

import com.evolveum.axiom.lang.antlr.query.AxiomQueryParser;
import com.evolveum.axiom.lang.antlr.query.AxiomQueryLexer;
Expand All @@ -12,7 +10,7 @@
public class AxiomQuerySource {

private final AxiomQueryParser.RootContext root;
private List<AxiomQueryError> syntaxErrorList;
private final List<AxiomQueryError> syntaxErrorList;

public AxiomQuerySource(AxiomQueryParser.RootContext root, List<AxiomQueryError> syntaxErrorList) {
this.root = root;
Expand All @@ -23,19 +21,19 @@ public static final AxiomQuerySource from(String query) {
CodePointCharStream stream = CharStreams.fromString(query);
AxiomQueryLexer lexer = new AxiomQueryLexer(stream);
AxiomQueryParser parser = new AxiomQueryParser(new CommonTokenStream(lexer));
AxiomQueryErrorListener axiomQueryErrorListener = new AxiomQueryErrorListener();
AxiomQuerySyntaxErrorListener axiomQuerySyntaxErrorListener = new AxiomQuerySyntaxErrorListener();
// DO NOT log to STDIN
lexer.removeErrorListeners();
lexer.addErrorListener(axiomQueryErrorListener);
lexer.addErrorListener(axiomQuerySyntaxErrorListener);
parser.removeErrorListeners();
parser.addErrorListener(axiomQueryErrorListener);
parser.addErrorListener(axiomQuerySyntaxErrorListener);

var root = parser.root();
if (root.filter() == null) {
throw new IllegalArgumentException("Unable to parse query: " + query);
}

return new AxiomQuerySource(root, axiomQueryErrorListener.errorList);
return new AxiomQuerySource(root, axiomQuerySyntaxErrorListener.errorList);
}

public AxiomQueryParser.RootContext root() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,21 @@
package com.evolveum.axiom.lang.antlr;

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;

import java.util.ArrayList;
import java.util.List;

/**
* Created by Dominik.
*/
public class AxiomQueryErrorListener extends BaseErrorListener {

public class AxiomQuerySyntaxErrorListener extends BaseErrorListener {
public List<AxiomQueryError> errorList = new ArrayList<>();

@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
int line, int charPositionInLine,
String msg, RecognitionException e)
{
String sourceName = recognizer.getInputStream().getSourceName();

if (!sourceName.isEmpty()) {
sourceName = String.format("%s:%d:%d: ", sourceName, line, charPositionInLine);
}

errorList.add(new AxiomQueryError(recognizer, offendingSymbol, line, charPositionInLine, 0, msg, e));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ <ID extends ItemDefinition> ComparisonResult compareDefinitions(@NotNull ID def1

ItemDefinition<?> createAdHocDefinition(QName elementName, QName typeName, int minOccurs, int maxOccurs);

List<String> getObjectTypeListByClassType(@NotNull Class<?> classType);

interface InvalidationListener {
void invalidate();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,166 @@
import com.evolveum.axiom.lang.antlr.query.AxiomQueryParser;
import com.evolveum.axiom.lang.antlr.query.AxiomQueryParser.*;
import com.evolveum.axiom.lang.antlr.query.AxiomQueryParserBaseVisitor;

import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.TypeDefinition;
import com.evolveum.midpoint.prism.impl.marshaller.ItemPathHolder;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.jetbrains.annotations.NotNull;

import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
* Created by Dominik.
*/
public class AxiomQueryCompletionVisitor extends AxiomQueryParserBaseVisitor<Object> {
private ParseTree lastNode = null;
private final SchemaRegistry schemaRegistry;
private ParseTree lastSeparator = null;
private ParseTree lastType = null;

public AxiomQueryCompletionVisitor(PrismContext prismContext) {
schemaRegistry = prismContext.getSchemaRegistry();
}

@Override
public Object visitTerminal(TerminalNode node) {
// set lastNode if visiting a terminal node
if (node.getSymbol().getType() != AxiomQueryParser.EOF) {
lastNode = node;
if (node.getSymbol().getType() == AxiomQueryParser.SEP) {
lastSeparator = node;
}

return null;
}

@Override
public Object visitChildren(RuleNode node) {
// set lastNode if visiting a rule node
lastNode = node;
return super.visitChildren(node);
public Object visitErrorNode(ErrorNode node) {
return super.visitErrorNode(node);
}

@Override
public Object visitItemFilter(ItemFilterContext ctx) {
for (int i = ctx.getChildCount() - 1; i >= 0; i--) {
if (ctx.getChild(i).getText().equals(FilterNames.TYPE.getLocalPart())) lastType = ctx.getChild(i + 2);
if (ctx.getChild(i).getText().equals(FilterNames.META_TYPE)) lastType = ctx.getChild(i + 4);
public Object visitItemComponent(ItemComponentContext ctx) {

if (schemaRegistry.findTypeDefinitionByType(new QName(ctx.getText())) != null) {
lastType = ctx;
}

return super.visitItemFilter(ctx);
return super.visitItemComponent(ctx);
}

public ParseTree getLastNode() {
public List<String> generateSuggestion() {
List<String> suggestions = new ArrayList<>();
ParseTree lastNode = getLastNode();

// generate suggestion
if (lastNode instanceof AxiomQueryParser.ItemPathComponentContext ctx) {
suggestions = getFilters(lastNode.getText());
suggestions.add(FilterNames.NOT.getLocalPart());
} else if (lastNode instanceof AxiomQueryParser.FilterNameContext ctx) {
// TODO maybe to add suggestion for value
// value for @type || . type
if (findNode(ctx).getChild(0).getText().equals(FilterNames.META_TYPE) || ctx.getText().equals(FilterNames.TYPE.getLocalPart())) {
TypeDefinition typeDefinition = schemaRegistry.findTypeDefinitionByType(new QName(lastType.getText()));
suggestions = schemaRegistry.getObjectTypeListByClassType(typeDefinition.getCompileTimeClass());
}
// value for @path
if (findNode(ctx).getChild(0).getText().equals(FilterNames.META_PATH)) {
suggestions = getAllPath();
}
// value for @relation
if (ctx.getText().equals(FilterNames.META_RELATION)) {
// TODO add value for @relation to suggestions list
}

if (ctx.getText().equals(FilterNames.MATCHES.getLocalPart()) || ctx.getText().equals(FilterNames.REFERENCED_BY.getLocalPart())) {
suggestions.add("(");
}
} else if (lastNode instanceof AxiomQueryParser.GenFilterContext || lastNode instanceof AxiomQueryParser.DescendantPathContext) {
suggestions = getFilters(lastNode.getText());
suggestions.add(FilterNames.NOT.getLocalPart());
} else if (lastNode instanceof AxiomQueryParser.SubfilterOrValueContext ctx) {
suggestions.add(FilterNames.AND.getLocalPart());
suggestions.add(FilterNames.OR.getLocalPart());
} else if (lastNode instanceof TerminalNode ctx) {
if (ctx.getSymbol().getType() == AxiomQueryParser.SEP || ctx.getSymbol().getType() == AxiomQueryParser.AND_KEYWORD || ctx.getSymbol().getType() == AxiomQueryParser.OR_KEYWORD) {
suggestions = getAllPath();
suggestions.add(".");
}
} else if (lastNode instanceof ErrorNode ctx) {
// TODO solve Error token
}

return suggestions;
}

private ParseTree getLastNode() {
int separatorIndex = -1;

if (lastSeparator == null) return null;

ParseTree lastSeparatorParent = lastSeparator.getParent();

for (int i = 0; i < lastSeparatorParent.getChildCount(); i++) {
if (lastSeparatorParent.getChild(i) instanceof TerminalNode terminalNode && terminalNode.getSymbol().getType() == AxiomQueryParser.SEP) {
separatorIndex = i;
}
}

if (separatorIndex > 0) separatorIndex = separatorIndex - 1;

ParseTree lastNode = lastSeparatorParent.getChild(separatorIndex);
int count = lastSeparatorParent.getChild(separatorIndex).getChildCount();

while (lastNode.getChild(count - 1) != null) {
lastNode = lastNode.getChild(count - 1);
count = lastNode.getChildCount();
}

while (lastNode.getParent().getChildCount() == 1) {
lastNode = lastNode.getParent();
}

return lastNode;
}

public ParseTree getLastType() {
return lastType;
private ParseTree findNode(ParseTree parseTree) {
while (parseTree.getChildCount() == 1) {
parseTree = parseTree.getParent();
}

return parseTree;
}

private ParseTree getNextToken(@NotNull ParserRuleContext ctx) {
int count = ctx.getChildCount();
return count >= 1 ? ctx.getChild(count + 1) : null;
}

private ParseTree getPreviousToken(@NotNull ParserRuleContext ctx) {
int count = ctx.getChildCount();
return count >= 1 ? ctx.getChild(count - 1) : null;
}

private List<String> getAllPath() {
TypeDefinition typeDefinition = schemaRegistry.findTypeDefinitionByType(new QName(lastType.getText()));
PrismObjectDefinition<?> objectDefinition = schemaRegistry.findObjectDefinitionByCompileTimeClass((Class) typeDefinition.getCompileTimeClass());
return objectDefinition.getItemNames().stream().map(QName::getLocalPart).collect(Collectors.toList());
}

private List<String> getFilters(@NotNull String stringItemPath) {
ItemPath itemPath = ItemPathHolder.parseFromString(stringItemPath);
TypeDefinition typeDefinition = schemaRegistry.findTypeDefinitionByType(new QName(lastType.getText()));
PrismObjectDefinition<?> objectDefinition = schemaRegistry.findObjectDefinitionByCompileTimeClass((Class) typeDefinition.getCompileTimeClass());
ItemDefinition<?> itemDefinition = objectDefinition.findItemDefinition(itemPath, ItemDefinition.class);
return FilterNamesProvider.findFilterNamesByItemDefinition(itemDefinition, new FilterContext());
}
}

0 comments on commit d4f0020

Please sign in to comment.