Skip to content

Commit d1741a1

Browse files
committed
Introduce Nullability API.
1 parent 6a1580c commit d1741a1

File tree

8 files changed

+626
-5
lines changed

8 files changed

+626
-5
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Copyright 2024 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+
* https://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.util;
17+
18+
import java.lang.reflect.Method;
19+
import java.lang.reflect.Parameter;
20+
21+
import org.springframework.core.MethodParameter;
22+
23+
/**
24+
* Provides access to nullability declarations of methods and parameters, usually obtained from a source such as a
25+
* {@link Class} or {@link Method}.
26+
* <p>
27+
* An application expresses nullability rules ideally expressed on the top-most element such as the package to let all
28+
* inner elements participate in the defaults. Individual elements such as methods or parameters can be annotated with
29+
* non-null annotations to express deviation from the default rule.
30+
* <p>
31+
* Nullability can be defined on various levels: Methods, (inner) classes, packages. We consider these as declaration
32+
* anchors. Introspection of nullability traverses declaration anchor trees in their logical order (i.e. a class
33+
* contains methods, an enclosing class contains inner classes, a package contains classes) to inherit nullability rules
34+
* if the particular method or parameter does not declare nullability rules.
35+
* <p>
36+
* A component might be interested on whether nullability is declared and if so, whether the particular element is
37+
* nullable or non-null.
38+
* <p>
39+
* Here are some typical examples:
40+
*
41+
* <pre class="code">
42+
* // is an nullability declared for a Method return type
43+
* Nullability nullability = Nullability.forMethodReturnType(method);
44+
* nullability.isDeclared();
45+
* nullability.isNullable();
46+
* nullability.isNonNull();
47+
*
48+
* // introspect multiple elements for their nullability in the scope of a class/package.
49+
* Nullability.Introspector introspector = Nullability.introspect(NonNullOnPackage.class);
50+
* Nullability nullability = introspector.forReturnType(method);
51+
* </pre>
52+
* <p>
53+
* <b>NOTE: The Nullability API is primarily intended for framework components that want to introspect nullability
54+
* declarations, for example to validate input or output.</b>
55+
*
56+
* @author Mark Paluch
57+
*/
58+
public interface Nullability {
59+
60+
/**
61+
* Determine if nullability declaration is present on the source.
62+
*
63+
* @return {@code true} if the source (or any of its declaration anchors) defines nullability rules.
64+
*/
65+
boolean isDeclared();
66+
67+
/**
68+
* Determine if the source is nullable.
69+
*
70+
* @return {@code true} if the source (or any of its declaration anchors) is nullable.
71+
*/
72+
boolean isNullable();
73+
74+
/**
75+
* Determine if the source is non-nullable.
76+
*
77+
* @return {@code true} if the source (or any of its declaration anchors) is non-nullable.
78+
*/
79+
boolean isNonNull();
80+
81+
/**
82+
* Creates a new {@link Nullability} instance by introspecting the {@link MethodParameter}.
83+
*
84+
* @param parameter the source method parameter.
85+
* @return a {@code Nullability} instance containing the element's nullability declaration.
86+
*/
87+
static Nullability from(MethodParameter parameter) {
88+
return introspect(parameter.getContainingClass()).forParameter(parameter);
89+
}
90+
91+
/**
92+
* Creates a new {@link Nullability} instance by introspecting the {@link Method} return type.
93+
*
94+
* @param method the source method.
95+
* @return a {@code Nullability} instance containing the element's nullability declaration.
96+
*/
97+
static Nullability forMethodReturnType(Method method) {
98+
return introspect(method.getDeclaringClass()).forReturnType(method);
99+
}
100+
101+
/**
102+
* Creates a new {@link Nullability} instance by introspecting the {@link Parameter method parameter}.
103+
*
104+
* @param parameter the source method parameter.
105+
* @return a {@code Nullability} instance containing the element's nullability declaration.
106+
*/
107+
static Nullability forMethodParameter(Parameter parameter) {
108+
return introspect(parameter.getDeclaringExecutable().getDeclaringClass()).forParameter(parameter);
109+
}
110+
111+
/**
112+
* Creates introspector using the given {@link Class} as declaration anchor.
113+
*
114+
* @param cls the source class.
115+
* @return a {@code Introspector} instance considering nullability declarations from the {@link Class} and package.
116+
*/
117+
static Introspector introspect(Class<?> cls) {
118+
return new NullabilityIntrospector(cls);
119+
}
120+
121+
/**
122+
* Creates introspector using the given {@link Package} as declaration anchor.
123+
*
124+
* @param pkg the source package.
125+
* @return a {@code Introspector} instance considering nullability declarations from package.
126+
*/
127+
static Introspector introspect(Package pkg) {
128+
return new NullabilityIntrospector(pkg);
129+
}
130+
131+
/**
132+
* Nullability introspector to introspect multiple elements within the context of their source container.
133+
*/
134+
interface Introspector {
135+
136+
/**
137+
* Creates a new {@link Nullability} instance by introspecting the {@link MethodParameter}.
138+
* <p>
139+
* If the method parameter does not declare any nullability rules, then introspection falls back to the source
140+
* container that was used to create the introspector.
141+
*
142+
* @param parameter the source method parameter.
143+
* @return a {@code Nullability} instance containing the element's nullability declaration.
144+
*/
145+
default Nullability forParameter(MethodParameter parameter) {
146+
return parameter.getParameterIndex() == -1 ? forReturnType(parameter.getMethod())
147+
: forParameter(parameter.getParameter());
148+
}
149+
150+
/**
151+
* Creates a new {@link Nullability} instance by introspecting the {@link Method} return type.
152+
* <p>
153+
* If the method parameter does not declare any nullability rules, then introspection falls back to the source
154+
* container that was used to create the introspector.
155+
*
156+
* @param method the source method.
157+
* @return a {@code Nullability} instance containing the element's nullability declaration.
158+
*/
159+
Nullability forReturnType(Method method);
160+
161+
/**
162+
* Creates a new {@link Nullability} instance by introspecting the {@link MethodParameter}.
163+
* <p>
164+
* If the method parameter does not declare any nullability rules, then introspection falls back to the source
165+
* container that was used to create the introspector.
166+
*
167+
* @param parameter the source method parameter.
168+
* @return a {@code Nullability} instance containing the element's nullability declaration.
169+
*/
170+
Nullability forParameter(Parameter parameter);
171+
172+
}
173+
174+
}

0 commit comments

Comments
 (0)