Skip to content

Commit

Permalink
Implement argument resolver for OffsetScrollPosition
Browse files Browse the repository at this point in the history
  • Loading branch information
quaff committed Jun 19, 2023
1 parent 8aff631 commit 78eb7f0
Show file tree
Hide file tree
Showing 9 changed files with 699 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2016-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.web;

import org.springframework.core.MethodParameter;
import org.springframework.data.domain.OffsetScrollPosition;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
* Argument resolver to extract a {@link OffsetScrollPosition} object from a {@link NativeWebRequest} for a particular
* {@link MethodParameter}. A {@link OffsetScrollPositionArgumentResolver} can either resolve {@link OffsetScrollPosition} itself or wrap another
* {@link OffsetScrollPositionArgumentResolver} to post-process {@link OffsetScrollPosition}.
*
* @since 3.2
* @author Yanming Zhou
* @see HandlerMethodArgumentResolver
*/
public interface OffsetScrollPositionArgumentResolver extends HandlerMethodArgumentResolver {

/**
* Resolves a {@link OffsetScrollPosition} method parameter into an argument value from a given request.
*
* @param parameter the method parameter to resolve. This parameter must have previously been passed to
* {@link #supportsParameter} which must have returned {@code true}.
* @param mavContainer the ModelAndViewContainer for the current request
* @param webRequest the current request
* @param binderFactory a factory for creating {@link WebDataBinder} instances
* @return the resolved argument value
*/
@NonNull
@Override
OffsetScrollPosition resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2013-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.web;

import org.springframework.core.MethodParameter;
import org.springframework.data.domain.OffsetScrollPosition;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.util.Arrays;

/**
* {@link HandlerMethodArgumentResolver} to automatically create {@link OffsetScrollPosition} instances from request parameters.
*
* @since 3.2
* @author Yanming Zhou
*/
public class OffsetScrollPositionHandlerMethodArgumentResolver extends OffsetScrollPositionHandlerMethodArgumentResolverSupport
implements OffsetScrollPositionArgumentResolver {

@Override
public boolean supportsParameter(MethodParameter parameter) {
return OffsetScrollPosition.class.equals(parameter.getParameterType());
}

@Override
public OffsetScrollPosition resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {

String[] offsetParameter = webRequest.getParameterValues(getOffsetParameter(parameter));
return parseParameterIntoOffsetScrollPosition(offsetParameter != null ? Arrays.asList(offsetParameter) : null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2017-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.web;

import org.springframework.core.MethodParameter;
import org.springframework.data.domain.OffsetScrollPosition;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;

/**
* Base class providing methods for handler method argument resolvers to create {@link OffsetScrollPosition} instances from request
* parameters.
*
* @since 3.2
* @author Yanming Zhou
* @see OffsetScrollPositionHandlerMethodArgumentResolver
* @see ReactiveOffsetScrollPositionHandlerMethodArgumentResolver
*/
public abstract class OffsetScrollPositionHandlerMethodArgumentResolverSupport {

private static final String DEFAULT_PARAMETER = "offset";

private static final String DEFAULT_QUALIFIER_DELIMITER = "_";

private String offsetParameter = DEFAULT_PARAMETER;

private String qualifierDelimiter = DEFAULT_QUALIFIER_DELIMITER;

/**
* Configure the request parameter to lookup offset information from. Defaults to {@code offset}.
*
* @param offsetParameter must not be {@literal null} or empty.
*/
public void setOffsetParameter(String offsetParameter) {

Assert.hasText(offsetParameter, "offsetParameter must not be null nor empty");
this.offsetParameter = offsetParameter;
}

/**
* Configures the delimiter used to separate the qualifier from the offset parameter. Defaults to {@code _}, so a
* qualified offset property would look like {@code qualifier_offset}.
*
* @param qualifierDelimiter the qualifier delimiter to be used or {@literal null} to reset to the default.
*/
public void setQualifierDelimiter(@Nullable String qualifierDelimiter) {
this.qualifierDelimiter = qualifierDelimiter == null ? DEFAULT_QUALIFIER_DELIMITER : qualifierDelimiter;
}

/**
* Returns the offset parameter to be looked up from the request. Potentially applies qualifiers to it.
*
* @param parameter can be {@literal null}.
* @return the offset parameter
*/
protected String getOffsetParameter(@Nullable MethodParameter parameter) {

StringBuilder builder = new StringBuilder();

String value = SpringDataAnnotationUtils.getQualifier(parameter);

if (StringUtils.hasLength(value)) {
builder.append(value);
builder.append(qualifierDelimiter);
}

return builder.append(offsetParameter).toString();
}

/**
* Parses the given source into a {@link OffsetScrollPosition} instance.
*
* @param source could be {@literal null} or empty.
* @return parsed OffsetScrollPosition
*/
OffsetScrollPosition parseParameterIntoOffsetScrollPosition(@Nullable List<String> source) {
// No parameter or Single empty parameter, e.g "offset="
if (source == null || source.size() == 1 && !StringUtils.hasText(source.get(0))) {
return ScrollPosition.offset();
}
try {
long offset = Long.parseLong(source.get(0));
return ScrollPosition.offset(offset);
} catch (NumberFormatException ex) {
return ScrollPosition.offset();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2017-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.web;

import org.springframework.core.MethodParameter;
import org.springframework.data.domain.OffsetScrollPosition;
import org.springframework.lang.NonNull;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;

import java.util.List;

/**
* Reactive {@link HandlerMethodArgumentResolver} to create {@link OffsetScrollPosition} instances from query string parameters.
*
* @since 3.2
* @author Yanming Zhou
*/
public class ReactiveOffsetScrollPositionHandlerMethodArgumentResolver extends OffsetScrollPositionHandlerMethodArgumentResolverSupport
implements SyncHandlerMethodArgumentResolver {

@Override
public boolean supportsParameter(MethodParameter parameter) {
return OffsetScrollPosition.class.equals(parameter.getParameterType());
}

@NonNull
@Override
public OffsetScrollPosition resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
ServerWebExchange exchange) {

List<String> offsetParameter = exchange.getRequest().getQueryParams().get(getOffsetParameter(parameter));

return parseParameterIntoOffsetScrollPosition(offsetParameter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2017-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.web.config;

import org.springframework.data.web.OffsetScrollPositionHandlerMethodArgumentResolver;

/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link OffsetScrollPositionHandlerMethodArgumentResolver} configuration.
*
* @since 2.0
* @author Yanming Zhou
*/
public interface OffsetScrollPositionHandlerMethodArgumentResolverCustomizer {

/**
* Customize the given {@link OffsetScrollPositionHandlerMethodArgumentResolver}.
*
* @param offsetResolver the {@link OffsetScrollPositionHandlerMethodArgumentResolver} to customize, will never be {@literal null}.
*/
void customize(OffsetScrollPositionHandlerMethodArgumentResolver offsetResolver);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.springframework.data.geo.format.PointFormatter;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.data.util.Lazy;
import org.springframework.data.web.OffsetScrollPositionHandlerMethodArgumentResolver;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.data.web.ProjectingJackson2HttpMessageConverter;
import org.springframework.data.web.ProxyingHandlerMethodArgumentResolver;
Expand All @@ -46,14 +47,16 @@

/**
* Configuration class to register {@link PageableHandlerMethodArgumentResolver},
* {@link SortHandlerMethodArgumentResolver} and {@link DomainClassConverter}.
* {@link SortHandlerMethodArgumentResolver}, {@link OffsetScrollPositionHandlerMethodArgumentResolver}
* and {@link DomainClassConverter}.
*
* @since 1.6
* @author Oliver Gierke
* @author Vedran Pavic
* @author Jens Schauder
* @author Mark Paluch
* @author Greg Turnquist
* @author Yanming Zhou
*/
@Configuration(proxyBeanMethods = false)
public class SpringDataWebConfiguration implements WebMvcConfigurer, BeanClassLoaderAware {
Expand All @@ -66,6 +69,7 @@ public class SpringDataWebConfiguration implements WebMvcConfigurer, BeanClassLo
private final Lazy<PageableHandlerMethodArgumentResolver> pageableResolver;
private final Lazy<PageableHandlerMethodArgumentResolverCustomizer> pageableResolverCustomizer;
private final Lazy<SortHandlerMethodArgumentResolverCustomizer> sortResolverCustomizer;
private final Lazy<OffsetScrollPositionHandlerMethodArgumentResolverCustomizer> offsetResolverCustomizer;

public SpringDataWebConfiguration(ApplicationContext context,
@Qualifier("mvcConversionService") ObjectFactory<ConversionService> conversionService) {
Expand All @@ -83,6 +87,8 @@ public SpringDataWebConfiguration(ApplicationContext context,
() -> context.getBeanProvider(PageableHandlerMethodArgumentResolverCustomizer.class).getIfAvailable());
this.sortResolverCustomizer = Lazy.of( //
() -> context.getBeanProvider(SortHandlerMethodArgumentResolverCustomizer.class).getIfAvailable());
this.offsetResolverCustomizer = Lazy.of( //
() -> context.getBeanProvider(OffsetScrollPositionHandlerMethodArgumentResolverCustomizer.class).getIfAvailable());
}

@Override
Expand All @@ -107,6 +113,14 @@ public SortHandlerMethodArgumentResolver sortResolver() {
return sortResolver;
}

@Bean
public OffsetScrollPositionHandlerMethodArgumentResolver offsetResolver() {

OffsetScrollPositionHandlerMethodArgumentResolver offsetResolver = new OffsetScrollPositionHandlerMethodArgumentResolver();
customizeOffsetResolver(offsetResolver);
return offsetResolver;
}

@Override
public void addFormatters(FormatterRegistry registry) {

Expand Down Expand Up @@ -165,6 +179,10 @@ protected void customizeSortResolver(SortHandlerMethodArgumentResolver sortResol
sortResolverCustomizer.getOptional().ifPresent(c -> c.customize(sortResolver));
}

protected void customizeOffsetResolver(OffsetScrollPositionHandlerMethodArgumentResolver offsetResolver) {
offsetResolverCustomizer.getOptional().ifPresent(c -> c.customize(offsetResolver));
}

private void forwardBeanClassLoader(BeanClassLoaderAware target) {

if (beanClassLoader != null) {
Expand Down
Loading

0 comments on commit 78eb7f0

Please sign in to comment.