Skip to content

Commit b06b4c1

Browse files
authored
Opencypher refactoring (#3367)
* chore: opencypher refactoring Due to high complexity, opencypher engine needs hard lifting * chore: continued refactoring of OpenCypher * chore: final refactoring of OpenCypher parser
1 parent 95820cc commit b06b4c1

21 files changed

Lines changed: 3610 additions & 460 deletions
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
* Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-FileCopyrightText: 2021-present Arcade Data Ltd (info@arcadedata.com)
17+
* SPDX-License-Identifier: Apache-2.0
18+
*/
19+
package com.arcadedb.query.opencypher.parser;
20+
21+
import com.arcadedb.query.opencypher.grammar.Cypher25Parser;
22+
23+
import java.util.HashMap;
24+
import java.util.Map;
25+
import java.util.function.Function;
26+
27+
/**
28+
* Dispatches clause contexts to their appropriate handlers.
29+
* Uses a strategy pattern with a dispatch table to eliminate cascading if/else statements.
30+
*
31+
* @author Luca Garulli (l.garulli@arcadedata.com)
32+
*/
33+
class ClauseDispatcher {
34+
35+
private final Map<Function<Cypher25Parser.ClauseContext, ?>, ClauseHandler> handlers = new HashMap<>();
36+
37+
ClauseDispatcher() {
38+
// Register all clause handlers
39+
register(Cypher25Parser.ClauseContext::matchClause, this::handleMatch);
40+
register(Cypher25Parser.ClauseContext::createClause, this::handleCreate);
41+
register(Cypher25Parser.ClauseContext::setClause, this::handleSet);
42+
register(Cypher25Parser.ClauseContext::deleteClause, this::handleDelete);
43+
register(Cypher25Parser.ClauseContext::mergeClause, this::handleMerge);
44+
register(Cypher25Parser.ClauseContext::unwindClause, this::handleUnwind);
45+
register(Cypher25Parser.ClauseContext::withClause, this::handleWith);
46+
register(Cypher25Parser.ClauseContext::returnClause, this::handleReturn);
47+
register(Cypher25Parser.ClauseContext::orderBySkipLimitClause, this::handleOrderBySkipLimit);
48+
register(Cypher25Parser.ClauseContext::callClause, this::handleCall);
49+
register(Cypher25Parser.ClauseContext::removeClause, this::handleRemove);
50+
register(Cypher25Parser.ClauseContext::foreachClause, this::handleForeach);
51+
register(Cypher25Parser.ClauseContext::subqueryClause, this::handleSubquery);
52+
}
53+
54+
/**
55+
* Dispatch a clause context to its appropriate handler.
56+
*
57+
* @param ctx the clause context
58+
* @param builder the statement builder
59+
* @param astBuilder the AST builder
60+
*/
61+
void dispatch(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder, final CypherASTBuilder astBuilder) {
62+
for (final Map.Entry<Function<Cypher25Parser.ClauseContext, ?>, ClauseHandler> entry : handlers.entrySet()) {
63+
final Object clauseCtx = entry.getKey().apply(ctx);
64+
if (clauseCtx != null) {
65+
entry.getValue().handle(ctx, builder, astBuilder);
66+
return;
67+
}
68+
}
69+
// No handler found - this should not happen with valid grammar
70+
}
71+
72+
private <T> void register(final Function<Cypher25Parser.ClauseContext, T> accessor, final ClauseHandler handler) {
73+
handlers.put(accessor, handler);
74+
}
75+
76+
// ============================================================================
77+
// Clause Handlers
78+
// ============================================================================
79+
80+
private void handleMatch(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
81+
final CypherASTBuilder astBuilder) {
82+
builder.addMatch(astBuilder.visitMatchClause(ctx.matchClause()));
83+
}
84+
85+
private void handleCreate(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
86+
final CypherASTBuilder astBuilder) {
87+
builder.setCreate(astBuilder.visitCreateClause(ctx.createClause()));
88+
}
89+
90+
private void handleSet(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
91+
final CypherASTBuilder astBuilder) {
92+
builder.setSet(astBuilder.visitSetClause(ctx.setClause()));
93+
}
94+
95+
private void handleDelete(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
96+
final CypherASTBuilder astBuilder) {
97+
builder.setDelete(astBuilder.visitDeleteClause(ctx.deleteClause()));
98+
}
99+
100+
private void handleMerge(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
101+
final CypherASTBuilder astBuilder) {
102+
builder.setMerge(astBuilder.visitMergeClause(ctx.mergeClause()));
103+
}
104+
105+
private void handleUnwind(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
106+
final CypherASTBuilder astBuilder) {
107+
builder.addUnwind(astBuilder.visitUnwindClause(ctx.unwindClause()));
108+
}
109+
110+
private void handleWith(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
111+
final CypherASTBuilder astBuilder) {
112+
builder.addWith(astBuilder.visitWithClause(ctx.withClause()));
113+
}
114+
115+
private void handleReturn(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
116+
final CypherASTBuilder astBuilder) {
117+
final Cypher25Parser.ReturnBodyContext body = ctx.returnClause().returnBody();
118+
builder.setReturn(astBuilder.visitReturnClause(ctx.returnClause()));
119+
120+
// Extract ORDER BY, SKIP, LIMIT from returnBody
121+
if (body.orderBy() != null)
122+
builder.setOrderBy(astBuilder.visitOrderBy(body.orderBy()));
123+
124+
if (body.skip() != null)
125+
builder.setSkip(astBuilder.visitSkip(body.skip()));
126+
127+
if (body.limit() != null)
128+
builder.setLimit(astBuilder.visitLimit(body.limit()));
129+
}
130+
131+
private void handleOrderBySkipLimit(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
132+
final CypherASTBuilder astBuilder) {
133+
final Cypher25Parser.OrderBySkipLimitClauseContext orderBySkipLimit = ctx.orderBySkipLimitClause();
134+
135+
if (orderBySkipLimit.orderBy() != null)
136+
builder.setOrderBy(astBuilder.visitOrderBy(orderBySkipLimit.orderBy()));
137+
138+
if (orderBySkipLimit.skip() != null)
139+
builder.setSkip(astBuilder.visitSkip(orderBySkipLimit.skip()));
140+
141+
if (orderBySkipLimit.limit() != null)
142+
builder.setLimit(astBuilder.visitLimit(orderBySkipLimit.limit()));
143+
}
144+
145+
private void handleCall(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
146+
final CypherASTBuilder astBuilder) {
147+
builder.addCall(astBuilder.visitCallClause(ctx.callClause()));
148+
}
149+
150+
private void handleRemove(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
151+
final CypherASTBuilder astBuilder) {
152+
builder.addRemove(astBuilder.visitRemoveClause(ctx.removeClause()));
153+
}
154+
155+
private void handleForeach(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
156+
final CypherASTBuilder astBuilder) {
157+
builder.addForeach(astBuilder.visitForeachClause(ctx.foreachClause()));
158+
}
159+
160+
private void handleSubquery(final Cypher25Parser.ClauseContext ctx, final StatementBuilder builder,
161+
final CypherASTBuilder astBuilder) {
162+
builder.addSubquery(astBuilder.visitSubqueryClause(ctx.subqueryClause()));
163+
}
164+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-FileCopyrightText: 2021-present Arcade Data Ltd (info@arcadedata.com)
17+
* SPDX-License-Identifier: Apache-2.0
18+
*/
19+
package com.arcadedb.query.opencypher.parser;
20+
21+
import com.arcadedb.query.opencypher.grammar.Cypher25Parser;
22+
23+
/**
24+
* Strategy interface for handling different Cypher clause types.
25+
* Each clause type (MATCH, CREATE, SET, etc.) has its own handler implementation.
26+
* This pattern replaces cascading if/else statements with a dispatch table.
27+
*
28+
* @author Luca Garulli (l.garulli@arcadedata.com)
29+
*/
30+
@FunctionalInterface
31+
public interface ClauseHandler {
32+
33+
/**
34+
* Process a clause context and add the result to the statement builder.
35+
*
36+
* @param ctx the clause context to process
37+
* @param builder the statement builder to populate
38+
* @param astBuilder the AST builder for visitor method access
39+
*/
40+
void handle(Cypher25Parser.ClauseContext ctx, StatementBuilder builder, CypherASTBuilder astBuilder);
41+
}

0 commit comments

Comments
 (0)