org.springframework.data.cassandra.core.mapping.MapIdProxyDelegate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-data-cassandra Show documentation
Show all versions of spring-data-cassandra Show documentation
Cassandra support for Spring Data
/*
* Copyright 2017-2022 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.cassandra.core.mapping;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Delegate class for dynamic proxies of id interfaces; delegates to {@link BasicMapId}.
*
* @see MapIdFactory#id(Class)
* @see MapIdFactory#id(Class, ClassLoader)
* @author Matthew T. Adams
* @author Mark Paluch
*/
class MapIdProxyDelegate implements InvocationHandler {
private static final Map MAP_ID_SIGNATURES;
static {
Method[] mapIdMethods = MapId.class.getMethods();
MAP_ID_SIGNATURES = Arrays.stream(mapIdMethods).map(m -> new Signature(m, true))
.collect(Collectors.toMap(o -> o, o -> o));
}
private MapId delegate = new BasicMapId();
private Class idInterface;
MapIdProxyDelegate(Class idInterface) {
this.idInterface = idInterface;
}
@Nullable
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (isMapIdMethod(method)) {
return method.invoke(delegate, args);
}
if (args != null && args.length > 1) {
throw new IllegalArgumentException(
String.format("Method [%s] on interface [%s] must take zero or one argument", method, idInterface));
}
boolean isSetter = args != null && args.length == 1;
if (isSetter) {
invokeSetter(method, args[0]);
return void.class.equals(method.getReturnType()) ? null : proxy;
}
return invokeGetter(method);
}
private boolean isMapIdMethod(Method method) {
return MAP_ID_SIGNATURES.containsKey(new Signature(method, true));
}
private Object invokeGetter(Method method) {
String name = method.getName();
if (name.startsWith("get")) {
if (name.length() == 3) {
throw new IllegalArgumentException(String.format("Method [%s] on interface [%s] must be of form "
+ "' get()' or " + "' ()'", name, idInterface));
}
name = StringUtils.uncapitalize(name.substring(3));
}
return delegate.get(name);
}
private void invokeSetter(Method method, @Nullable Object value) {
String name = method.getName();
int minLength = 1;
boolean isSet = name.startsWith("set");
boolean isWith = name.startsWith("with");
minLength += isSet ? 3 : isWith ? 4 : 0;
int length = name.length();
if (isSet || isWith) {
if (length < minLength) {
throw new IllegalArgumentException(String.format(
"Method [%s] on interface [%s] must be of form " + "' set()', "
+ "' with()' or "
+ "' ()'",
name, idInterface));
}
name = StringUtils.uncapitalize(name.substring(minLength - 1));
}
if (value == null) {
delegate.put(name, null);
return;
}
delegate.put(name, value);
}
static class Signature {
private String name;
private @Nullable Class[] argTypes;
private @Nullable Class returnType;
Signature(Method method, boolean includeReturnType) {
this(method.getName(), method.getParameterTypes(), includeReturnType ? method.getReturnType() : null);
}
Signature(String name, Class[] argTypes, @Nullable Class returnType) {
this.name = name;
this.argTypes = argTypes;
this.returnType = returnType;
}
@Override
public String toString() {
return String.format("%s %s(%s)", returnType, name, Arrays.toString(argTypes));
}
@Override
public boolean equals(Object that) {
if (that == null) {
return false;
}
if (this == that) {
return true;
}
if (!(that instanceof Signature)) {
return false;
}
Signature that_ = (Signature) that;
if (!this.name.equals(that_.name)) {
return false;
}
if ((this.argTypes == null && that_.argTypes != null) || (this.argTypes != null && that_.argTypes == null)) {
return false;
}
if (this.argTypes != null) {
if (this.argTypes.length != that_.argTypes.length) {
return false;
}
for (int i = 0; i < this.argTypes.length; i++) {
if (!this.argTypes[i].equals(that_.argTypes[i])) {
return false;
}
}
}
if (this.returnType == null) {
return that_.returnType == null;
}
return this.returnType.equals(that_.returnType);
}
@Override
public int hashCode() {
int hash = 37 ^ name.hashCode();
if (argTypes != null) {
for (Class c : argTypes) {
hash ^= c.hashCode();
}
}
if (returnType != null) {
hash ^= returnType.hashCode();
}
return hash;
}
}
}