All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.data.relational.core.sql.render.DelegatingVisitor Maven / Gradle / Ivy

There is a newer version: 3.3.4
Show newest version
/*
 * Copyright 2019-2023 the original author or authors.
 *
 * 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
 *
 *      https://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.
 */
package org.springframework.data.relational.core.sql.render;

import java.util.Stack;

import org.springframework.data.relational.core.sql.Visitable;
import org.springframework.data.relational.core.sql.Visitor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * Abstract base class for delegating {@link Visitor} implementations. This class implements a delegation pattern using
 * visitors. A delegating {@link Visitor} can implement {@link #doEnter(Visitable)} and {@link #doLeave(Visitable)}
 * methods to provide its functionality.
 * 

*

Delegation

Typically, a {@link Visitor} is scoped to a single responsibility. If a {@link Visitor segment} * requires {@link #doEnter(Visitable) processing} that is not directly implemented by the visitor itself, the current * {@link Visitor} can delegate processing to a {@link DelegatingVisitor delegate}. Once a delegation is installed, the * {@link DelegatingVisitor delegate} is used as {@link Visitor} for the current and all subsequent items until it * {@link #doLeave(Visitable) signals} that it is no longer responsible. *

* Nested visitors are required to properly signal once they are no longer responsible for a {@link Visitor segment} to * step back from the delegation. Otherwise, parents are no longer involved in the visitation. *

* Delegation is recursive and limited by the stack size. * * @author Mark Paluch * @since 1.1 * @see FilteredSubtreeVisitor * @see TypedSubtreeVisitor */ abstract class DelegatingVisitor implements Visitor { private Stack delegation = new Stack<>(); /** * Invoked for a {@link Visitable segment} when entering the segment. *

* This method can signal whether it is responsible for handling the {@link Visitor segment} or whether the segment * requires delegation to a sub-{@link Visitor}. When delegating to a sub-{@link Visitor}, {@link #doEnter(Visitable)} * is called on the {@link DelegatingVisitor delegate}. * * @param segment must not be {@literal null}. * @return */ @Nullable public abstract Delegation doEnter(Visitable segment); /* * (non-Javadoc) * @see org.springframework.data.relational.core.sql.Visitor#enter(org.springframework.data.relational.core.sql.Visitable) */ @Override public final void enter(Visitable segment) { if (delegation.isEmpty()) { Delegation visitor = doEnter(segment); Assert.notNull(visitor, () -> String.format("Visitor must not be null. Caused by %s.doEnter(…)", getClass().getName())); Assert.state(!visitor.isLeave(), () -> String.format("Delegation indicates leave. Caused by %s.doEnter(…)", getClass().getName())); if (visitor.isDelegate()) { delegation.push(visitor.getDelegate()); visitor.getDelegate().enter(segment); } } else { delegation.peek().enter(segment); } } /** * Invoked for a {@link Visitable segment} when leaving the segment. *

* This method can signal whether this {@link Visitor} should remain responsible for handling subsequent * {@link Visitor segments} or whether it should step back from delegation. When stepping back from delegation, * {@link #doLeave(Visitable)} is called on the {@link DelegatingVisitor parent delegate}. * * @param segment must not be {@literal null}. * @return */ public abstract Delegation doLeave(Visitable segment); /* * (non-Javadoc) * @see org.springframework.data.relational.core.sql.Visitor#leave(org.springframework.data.relational.core.sql.Visitable) */ public final void leave(Visitable segment) { doLeave0(segment); } private Delegation doLeave0(Visitable segment) { if (delegation.isEmpty()) { return doLeave(segment); } else { DelegatingVisitor visitor = delegation.peek(); while (visitor != null) { Delegation result = visitor.doLeave0(segment); Assert.notNull(visitor, () -> String.format("Visitor must not be null. Caused by %s.doLeave(…)", getClass().getName())); if (visitor == this) { if (result.isLeave()) { return delegation.isEmpty() ? Delegation.leave() : Delegation.retain(); } return Delegation.retain(); } if (result.isRetain()) { return result; } if (result.isLeave()) { if (!delegation.isEmpty()) { delegation.pop(); } if (!delegation.isEmpty()) { visitor = delegation.peek(); } else { visitor = this; } } } } return Delegation.leave(); } /** * Value object to control delegation. */ static class Delegation { private static Delegation RETAIN = new Delegation(true, false, null); private static Delegation LEAVE = new Delegation(false, true, null); private final boolean retain; private final boolean leave; private final @Nullable DelegatingVisitor delegate; private Delegation(boolean retain, boolean leave, @Nullable DelegatingVisitor delegate) { this.retain = retain; this.leave = leave; this.delegate = delegate; } public static Delegation retain() { return RETAIN; } public static Delegation leave() { return LEAVE; } public static Delegation delegateTo(DelegatingVisitor visitor) { return new Delegation(false, false, visitor); } boolean isDelegate() { return delegate != null; } boolean isRetain() { return retain; } boolean isLeave() { return leave; } DelegatingVisitor getDelegate() { Assert.state(isDelegate(), "No delegate available"); return delegate; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy