[jOOQ/jOOQ#11520] Add CallbackVisitListener

This commit is contained in:
Lukas Eder 2021-02-25 12:40:23 +01:00
parent b483e0bd70
commit cddd67ff95
8 changed files with 214 additions and 52 deletions

View File

@ -37,7 +37,6 @@
*/
package org.jooq;
import java.io.Serializable;
import java.util.EventListener;
import java.util.function.Consumer;
@ -68,7 +67,7 @@ import org.jooq.impl.CallbackRecordListener;
*
* @author Lukas Eder
*/
public interface RecordListener extends EventListener, Serializable {
public interface RecordListener extends EventListener {
/**
* Called before storing an <code>UpdatableRecord</code>.

View File

@ -49,7 +49,7 @@ import org.jooq.impl.CallbackTransactionListener;
*
* @author Lukas Eder
*/
public interface TransactionListener extends Serializable {
public interface TransactionListener {
/**
* Called before {@link TransactionProvider#begin(TransactionContext)}.

View File

@ -39,6 +39,9 @@ package org.jooq;
import java.sql.PreparedStatement;
import java.util.EventListener;
import java.util.function.Consumer;
import org.jooq.impl.CallbackVisitListener;
/**
* A listener for {@link QueryPart} traversal events.
@ -143,4 +146,36 @@ public interface VisitListener extends EventListener {
* @see Context#visit(QueryPart)
*/
void visitEnd(VisitContext context);
/**
* Create a {@link VisitListener} with a {@link #onClauseStart(Consumer)}
* implementation.
*/
static CallbackVisitListener onClauseStart(Consumer<? super VisitContext> onClauseStart) {
return new CallbackVisitListener().onClauseStart(onClauseStart);
}
/**
* Create a {@link VisitListener} with a {@link #onClauseEnd(Consumer)}
* implementation.
*/
static CallbackVisitListener onClauseEnd(Consumer<? super VisitContext> onClauseEnd) {
return new CallbackVisitListener().onClauseEnd(onClauseEnd);
}
/**
* Create a {@link VisitListener} with a {@link #onVisitStart(Consumer)}
* implementation.
*/
static CallbackVisitListener onVisitStart(Consumer<? super VisitContext> onVisitStart) {
return new CallbackVisitListener().onVisitStart(onVisitStart);
}
/**
* Create a {@link VisitListener} with a {@link #onClauseStart(Consumer)}
* implementation.
*/
static CallbackVisitListener onVisitEnd(Consumer<? super VisitContext> onVisitEnd) {
return new CallbackVisitListener().onVisitEnd(onVisitEnd);
}
}

View File

@ -56,11 +56,6 @@ import org.jooq.RecordListener;
*/
public final class CallbackRecordListener implements RecordListener {
/**
* Generated UID
*/
private static final long serialVersionUID = -4135358887698253754L;
private final Consumer<? super RecordContext> onStoreStart;
private final Consumer<? super RecordContext> onStoreEnd;
private final Consumer<? super RecordContext> onInsertStart;

View File

@ -58,11 +58,6 @@ import org.jooq.TransactionListener;
*/
public final class CallbackTransactionListener implements TransactionListener {
/**
* Generated UID
*/
private static final long serialVersionUID = -4135358887698253754L;
private final Consumer<? super TransactionContext> onBeginStart;
private final Consumer<? super TransactionContext> onBeginEnd;
private final Consumer<? super TransactionContext> onCommitStart;

View File

@ -0,0 +1,140 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import java.util.function.Consumer;
import org.jooq.VisitContext;
import org.jooq.VisitListener;
/**
* A {@link VisitListener} that allows for functional composition.
* <p>
* For example: <code><pre>
* VisitListener listener = VisitListener
* .onVisitStart(ctx -&gt; something())
* .onVisitEnd(ctx -&gt; something());
* </pre></code>
*
* @author Lukas Eder
*/
public final class CallbackVisitListener implements VisitListener {
private final Consumer<? super VisitContext> onClauseStart;
private final Consumer<? super VisitContext> onClauseEnd;
private final Consumer<? super VisitContext> onVisitStart;
private final Consumer<? super VisitContext> onVisitEnd;
public CallbackVisitListener() {
this(null, null, null, null);
}
private CallbackVisitListener(
Consumer<? super VisitContext> onClauseStart,
Consumer<? super VisitContext> onClauseEnd,
Consumer<? super VisitContext> onVisitStart,
Consumer<? super VisitContext> onVisitEnd
) {
this.onClauseStart = onClauseStart;
this.onClauseEnd = onClauseEnd;
this.onVisitStart = onVisitStart;
this.onVisitEnd = onVisitEnd;
}
@Override
public final void clauseStart(VisitContext context) {
if (onClauseStart != null)
onClauseStart.accept(context);
}
@Override
public final void clauseEnd(VisitContext context) {
if (onClauseEnd != null)
onClauseEnd.accept(context);
}
@Override
public final void visitStart(VisitContext context) {
if (onVisitStart != null)
onVisitStart.accept(context);
}
@Override
public final void visitEnd(VisitContext context) {
if (onVisitEnd != null)
onVisitEnd.accept(context);
}
public final CallbackVisitListener onClauseStart(Consumer<? super VisitContext> newOnClauseStart) {
return new CallbackVisitListener(
newOnClauseStart,
onClauseEnd,
onVisitStart,
onVisitEnd
);
}
public final CallbackVisitListener onClauseEnd(Consumer<? super VisitContext> newOnClauseEnd) {
return new CallbackVisitListener(
onClauseStart,
newOnClauseEnd,
onVisitStart,
onVisitEnd
);
}
public final CallbackVisitListener onVisitStart(Consumer<? super VisitContext> newOnVisitStart) {
return new CallbackVisitListener(
onClauseStart,
onClauseEnd,
newOnVisitStart,
onVisitEnd
);
}
public final CallbackVisitListener onVisitEnd(Consumer<? super VisitContext> newOnVisitEnd) {
return new CallbackVisitListener(
onClauseStart,
onClauseEnd,
onVisitStart,
newOnVisitEnd
);
}
}

View File

@ -55,6 +55,7 @@ import static org.jooq.SQLDialect.MYSQL;
// ...
// ...
// ...
import static org.jooq.VisitListener.onVisitStart;
import static org.jooq.conf.ParseWithMetaLookups.IGNORE_ON_FAILURE;
import static org.jooq.conf.ParseWithMetaLookups.THROW_ON_FAILURE;
import static org.jooq.conf.SettingsTools.parseLocale;
@ -578,6 +579,7 @@ import org.jooq.User;
// ...
// ...
import org.jooq.VisitContext;
import org.jooq.VisitListener;
import org.jooq.WindowBeforeOverStep;
import org.jooq.WindowDefinition;
import org.jooq.WindowFromFirstLastStep;
@ -13392,17 +13394,14 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
// [#8722] TODO Replace this by a public SPI
// [#11054] Use a VisitListener to find actual Params in the expression tree,
// which may have more refined DataTypes attached to them, from context
dsl.configuration().derive(new DefaultVisitListener() {
@Override
public void visitStart(VisitContext context) {
if (context.queryPart() instanceof Param) {
Param<?> p = (Param<?>) context.queryPart();
dsl.configuration().derive(onVisitStart(ctx -> {
if (ctx.queryPart() instanceof Param) {
Param<?> p = (Param<?>) ctx.queryPart();
if (!params.containsKey(p.getParamName()))
params.put(p.getParamName(), p);
}
if (!params.containsKey(p.getParamName()))
params.put(p.getParamName(), p);
}
}).dsl().render(result);
})).dsl().render(result);
for (String name : bindParams.keySet())
bindParamListener.accept(params.get(name));

View File

@ -37,6 +37,7 @@
*/
package org.jooq.impl;
import static org.jooq.VisitListener.onVisitStart;
import static org.jooq.impl.DSL.name;
import java.io.Reader;
@ -58,6 +59,7 @@ import org.jooq.Query;
import org.jooq.ResultQuery;
import org.jooq.Source;
import org.jooq.VisitContext;
import org.jooq.VisitListener;
import org.jooq.conf.RenderNameCase;
import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
@ -117,42 +119,39 @@ final class TranslatingMetaProvider implements MetaProvider {
final RenderNameCase nameCase = settings.getRenderNameCase();
final Locale locale = SettingsTools.interpreterLocale(ctx.settings());
if (nameCase != null && nameCase != RenderNameCase.AS_IS) {
ctx.configuration().set(new DefaultVisitListener() {
@Override
public void visitStart(VisitContext c) {
if (c.queryPart() instanceof Name) {
Name[] parts = ((Name) c.queryPart()).parts();
boolean changed = false;
ctx.configuration().set(onVisitStart(c -> {
if (c.queryPart() instanceof Name) {
Name[] parts = ((Name) c.queryPart()).parts();
boolean changed = false;
for (int i = 0; i < parts.length; i++) {
Name replacement = parts[i];
switch (nameCase) {
case LOWER_IF_UNQUOTED:
if (parts[i].quoted() == Quoted.QUOTED) break;
case LOWER:
replacement = DSL.quotedName(parts[i].first().toLowerCase(locale));
break;
for (int i = 0; i < parts.length; i++) {
Name replacement = parts[i];
switch (nameCase) {
case LOWER_IF_UNQUOTED:
if (parts[i].quoted() == Quoted.QUOTED) break;
case LOWER:
replacement = DSL.quotedName(parts[i].first().toLowerCase(locale));
break;
case UPPER_IF_UNQUOTED:
if (parts[i].quoted() == Quoted.QUOTED) break;
case UPPER:
replacement = DSL.quotedName(parts[i].first().toUpperCase(locale));
break;
case UPPER_IF_UNQUOTED:
if (parts[i].quoted() == Quoted.QUOTED) break;
case UPPER:
replacement = DSL.quotedName(parts[i].first().toUpperCase(locale));
break;
default:
break;
}
if (!replacement.equals(parts[i])) {
parts[i] = replacement;
changed = true;
}
default:
break;
}
if (!replacement.equals(parts[i])) {
parts[i] = replacement;
changed = true;
}
if (changed)
c.queryPart(DSL.name(parts));
}
if (changed)
c.queryPart(DSL.name(parts));
}
});
}));
}
}
catch (Exception e) {