Skip to content

Commit e85199c

Browse files
committed
DATAJDBC-309 - Refactoring.
Move SQL renderer to renderer package.
1 parent 8c627ac commit e85199c

26 files changed

+577
-306
lines changed

spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Conditions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public static Condition in(Expression columnOrExpression, Expression arg) {
7777
/**
7878
* Creates a {@code IN} {@link Condition clause} for a {@link Select subselect}.
7979
*
80-
* @param Column the column to compare.
80+
* @param column the column to compare.
8181
* @param subselect the subselect.
8282
* @return the {@link In} condition.
8383
*/
Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,11 @@
1-
/*
2-
* Copyright 2019 the original author or authors.
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-
*/
161

172
/**
18-
* Query Builder AST. Use {@link org.springframework.data.relational.core.sql.SQL} as entry point to create SQL objects. Objects and dependent objects created by the Query AST are immutable except for builders.
19-
* <p/> The Query Builder API is intended for framework usage to produce SQL required for framework operations.
3+
* Statement Builder implementation. Use {@link org.springframework.data.relational.core.sql.SQL} as entry point to create SQL objects. Objects and dependent objects created by the Query AST are immutable except for builders.
4+
* <p/> The Statement Builder API is intended for framework usage to produce SQL required for framework operations.
205
*/
216
@NonNullApi
7+
@NonNullFields
228
package org.springframework.data.relational.core.sql;
239

2410
import org.springframework.lang.NonNullApi;
11+
import org.springframework.lang.NonNullFields;
Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
import org.springframework.data.relational.core.sql.Visitable;
2222

2323
/**
24-
* {@link org.springframework.data.relational.core.sql.Visitor} rendering comparison {@link Condition}. Uses a {@link RenderTarget} to call back for render results.
24+
* {@link org.springframework.data.relational.core.sql.Visitor} rendering comparison {@link Condition}. Uses a
25+
* {@link RenderTarget} to call back for render results.
2526
*
2627
* @author Mark Paluch
2728
* @author Jens Schauder
@@ -40,26 +41,34 @@ class ComparisonVisitor extends FilteredSubtreeVisitor {
4041
this.comparator = " = ";
4142
}
4243

44+
/*
45+
* (non-Javadoc)
46+
* @see org.springframework.data.relational.core.sql.render.FilteredSubtreeVisitor#enterNested(org.springframework.data.relational.core.sql.Visitable)
47+
*/
4348
@Override
44-
DelegatingVisitor enterNested(Visitable segment) {
49+
Delegation enterNested(Visitable segment) {
4550

4651
if (segment instanceof Expression) {
4752
ExpressionVisitor visitor = new ExpressionVisitor();
4853
current = visitor;
49-
return visitor;
54+
return Delegation.delegateTo(visitor);
5055
}
5156

5257
if (segment instanceof Condition) {
5358
ConditionVisitor visitor = new ConditionVisitor();
5459
current = visitor;
55-
return visitor;
60+
return Delegation.delegateTo(visitor);
5661
}
5762

5863
throw new IllegalStateException("Cannot provide visitor for " + segment);
5964
}
6065

66+
/*
67+
* (non-Javadoc)
68+
* @see org.springframework.data.relational.core.sql.render.FilteredSubtreeVisitor#leaveNested(org.springframework.data.relational.core.sql.Visitable)
69+
*/
6170
@Override
62-
DelegatingVisitor leaveNested(Visitable segment) {
71+
Delegation leaveNested(Visitable segment) {
6372

6473
if (current != null) {
6574
if (part.length() != 0) {
@@ -73,8 +82,12 @@ DelegatingVisitor leaveNested(Visitable segment) {
7382
return super.leaveNested(segment);
7483
}
7584

85+
/*
86+
* (non-Javadoc)
87+
* @see org.springframework.data.relational.core.sql.render.FilteredSubtreeVisitor#leaveMatched(org.springframework.data.relational.core.sql.Visitable)
88+
*/
7689
@Override
77-
DelegatingVisitor leaveMatched(Visitable segment) {
90+
Delegation leaveMatched(Visitable segment) {
7891

7992
target.onRendered(part);
8093

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
import org.springframework.data.relational.core.sql.OrCondition;
2424

2525
/**
26-
* {@link org.springframework.data.relational.core.sql.Visitor} delegating {@link Condition} rendering to condition {@link org.springframework.data.relational.core.sql.Visitor}s.
26+
* {@link org.springframework.data.relational.core.sql.Visitor} delegating {@link Condition} rendering to condition
27+
* {@link org.springframework.data.relational.core.sql.Visitor}s.
2728
*
2829
* @author Mark Paluch
2930
* @author Jens Schauder
@@ -37,8 +38,19 @@ class ConditionVisitor extends TypedSubtreeVisitor<Condition> implements PartRen
3738

3839
private StringBuilder builder = new StringBuilder();
3940

41+
/*
42+
* (non-Javadoc)
43+
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#enterMatched(org.springframework.data.relational.core.sql.Visitable)
44+
*/
4045
@Override
41-
DelegatingVisitor enterMatched(Condition segment) {
46+
Delegation enterMatched(Condition segment) {
47+
48+
DelegatingVisitor visitor = getDelegation(segment);
49+
50+
return visitor != null ? Delegation.delegateTo(visitor) : Delegation.retain();
51+
}
52+
53+
private DelegatingVisitor getDelegation(Condition segment) {
4254

4355
if (segment instanceof AndCondition) {
4456
return new MultiConcatConditionVisitor((AndCondition) segment, builder::append);
@@ -60,9 +72,13 @@ DelegatingVisitor enterMatched(Condition segment) {
6072
return new InVisitor(builder::append);
6173
}
6274

63-
return this;
75+
return null;
6476
}
6577

78+
/*
79+
* (non-Javadoc)
80+
* @see org.springframework.data.relational.core.sql.render.PartRenderer#getRenderedPart()
81+
*/
6682
@Override
6783
public CharSequence getRenderedPart() {
6884
return builder;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
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+
package org.springframework.data.relational.core.sql.render;
17+
18+
import java.util.Stack;
19+
20+
import org.springframework.data.relational.core.sql.Visitable;
21+
import org.springframework.data.relational.core.sql.Visitor;
22+
import org.springframework.lang.Nullable;
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* Abstract base class for delegating {@link Visitor} implementations. This class implements a delegation pattern using
27+
* visitors. A delegating {@link Visitor} can implement {@link #doEnter(Visitable)} and {@link #doLeave(Visitable)}
28+
* methods to provide its functionality.
29+
* <p/>
30+
* <h3>Delegation</h3> Typically, a {@link Visitor} is scoped to a single responsibility. If a {@link Visitor segment}
31+
* requires {@link #doEnter(Visitable) processing} that is not directly implemented by the visitor itself, the current
32+
* {@link Visitor} can delegate processing to a {@link DelegatingVisitor delegate}. Once a delegation is installed, the
33+
* {@link DelegatingVisitor delegate} is used as {@link Visitor} for the current and all subsequent items until it
34+
* {@link #doLeave(Visitable) signals} that it is no longer responsible.
35+
* <p/>
36+
* Nested visitors are required to properly signal once they are no longer responsible for a {@link Visitor segment} to
37+
* step back from the delegation. Otherwise, parents are no longer involved in the visitation.
38+
* <p/>
39+
* Delegation is recursive and limited by the stack size.
40+
*
41+
* @author Mark Paluch
42+
* @see FilteredSubtreeVisitor
43+
* @see TypedSubtreeVisitor
44+
*/
45+
abstract class DelegatingVisitor implements Visitor {
46+
47+
private Stack<DelegatingVisitor> delegation = new Stack<>();
48+
49+
/**
50+
* Invoked for a {@link Visitable segment} when entering the segment.
51+
* <p/>
52+
* This method can signal whether it is responsible for handling the {@link Visitor segment} or whether the segment
53+
* requires delegation to a sub-{@link Visitor}. When delegating to a sub-{@link Visitor}, {@link #doEnter(Visitable)}
54+
* is called on the {@link DelegatingVisitor delegate}.
55+
*
56+
* @param segment must not be {@literal null}.
57+
* @return
58+
*/
59+
@Nullable
60+
public abstract Delegation doEnter(Visitable segment);
61+
62+
/*
63+
* (non-Javadoc)
64+
* @see org.springframework.data.relational.core.sql.Visitor#enter(org.springframework.data.relational.core.sql.Visitable)
65+
*/
66+
@Override
67+
public final void enter(Visitable segment) {
68+
69+
if (delegation.isEmpty()) {
70+
71+
Delegation visitor = doEnter(segment);
72+
Assert.notNull(visitor,
73+
() -> String.format("Visitor must not be null. Caused by %s.doEnter(…)", getClass().getName()));
74+
Assert.state(!visitor.isLeave(),
75+
() -> String.format("Delegation indicates leave. Caused by %s.doEnter(…)", getClass().getName()));
76+
77+
if (visitor.isDelegate()) {
78+
delegation.push(visitor.getDelegate());
79+
visitor.getDelegate().enter(segment);
80+
}
81+
} else {
82+
delegation.peek().enter(segment);
83+
}
84+
}
85+
86+
/**
87+
* Invoked for a {@link Visitable segment} when leaving the segment.
88+
* <p/>
89+
* This method can signal whether this {@link Visitor} should remain responsible for handling subsequent
90+
* {@link Visitor segments} or whether it should step back from delegation. When stepping back from delegation,
91+
* {@link #doLeave(Visitable)} is called on the {@link DelegatingVisitor parent delegate}.
92+
*
93+
* @param segment must not be {@literal null}.
94+
* @return
95+
*/
96+
public abstract Delegation doLeave(Visitable segment);
97+
98+
/*
99+
* (non-Javadoc)
100+
* @see org.springframework.data.relational.core.sql.Visitor#leave(org.springframework.data.relational.core.sql.Visitable)
101+
*/
102+
public final void leave(Visitable segment) {
103+
doLeave0(segment);
104+
}
105+
106+
private Delegation doLeave0(Visitable segment) {
107+
108+
if (delegation.isEmpty()) {
109+
return doLeave(segment);
110+
} else {
111+
112+
DelegatingVisitor visitor = delegation.peek();
113+
while (visitor != null) {
114+
115+
Delegation result = visitor.doLeave0(segment);
116+
Assert.notNull(visitor,
117+
() -> String.format("Visitor must not be null. Caused by %s.doLeave(…)", getClass().getName()));
118+
119+
if (visitor == this) {
120+
if (result.isLeave()) {
121+
return delegation.isEmpty() ? Delegation.leave() : Delegation.retain();
122+
}
123+
return Delegation.retain();
124+
}
125+
126+
if (result.isRetain()) {
127+
return result;
128+
}
129+
130+
if (result.isLeave()) {
131+
132+
if (!delegation.isEmpty()) {
133+
delegation.pop();
134+
}
135+
136+
if (!delegation.isEmpty()) {
137+
visitor = delegation.peek();
138+
} else {
139+
visitor = this;
140+
}
141+
}
142+
}
143+
}
144+
145+
return Delegation.leave();
146+
}
147+
148+
/**
149+
* Value object to control delegation.
150+
*/
151+
static class Delegation {
152+
153+
private static Delegation RETAIN = new Delegation(true, false, null);
154+
private static Delegation LEAVE = new Delegation(false, true, null);
155+
156+
private final boolean retain;
157+
private final boolean leave;
158+
159+
private final @Nullable DelegatingVisitor delegate;
160+
161+
private Delegation(boolean retain, boolean leave, @Nullable DelegatingVisitor delegate) {
162+
this.retain = retain;
163+
this.leave = leave;
164+
this.delegate = delegate;
165+
}
166+
167+
public static Delegation retain() {
168+
return RETAIN;
169+
}
170+
171+
public static Delegation leave() {
172+
return LEAVE;
173+
}
174+
175+
public static Delegation delegateTo(DelegatingVisitor visitor) {
176+
return new Delegation(false, false, visitor);
177+
}
178+
179+
boolean isDelegate() {
180+
return delegate != null;
181+
}
182+
183+
boolean isRetain() {
184+
return retain;
185+
}
186+
187+
boolean isLeave() {
188+
return leave;
189+
}
190+
191+
DelegatingVisitor getDelegate() {
192+
193+
Assert.state(isDelegate(), "No delegate available");
194+
return delegate;
195+
}
196+
}
197+
}

0 commit comments

Comments
 (0)