HHH-16389 Introduce checkerframework for nullness marking/checking and null check the JPA metamodel generator

This commit is contained in:
Christian Beikov 2023-04-05 13:59:39 +02:00
parent 0da73a27a5
commit 484cbfe720
19 changed files with 1081 additions and 127 deletions

View File

@ -25,6 +25,8 @@ plugins {
id 'org.hibernate.orm.database-service' apply false id 'org.hibernate.orm.database-service' apply false
id 'biz.aQute.bnd' version '6.3.1' apply false id 'biz.aQute.bnd' version '6.3.1' apply false
id 'org.checkerframework' version '0.6.25'
id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' id 'io.github.gradle-nexus.publish-plugin' version '1.1.0'
id 'idea' id 'idea'

View File

@ -0,0 +1,166 @@
// Checkerframework stubs for the jakarta.persistence module
package jakarta.persistence;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface AttributeConverter<X,Y> {
public @Nullable Y convertToDatabaseColumn(@Nullable X attribute);
public @Nullable X convertToEntityAttribute(@Nullable Y dbData);
}
public interface EntityManager extends AutoCloseable {
public <T> @Nullable T find(Class<T> entityClass, Object primaryKey);
public <T> @Nullable T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties);
public <T> @Nullable T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode);
public <T> @Nullable T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties);
}
public interface EntityManagerFactory extends AutoCloseable {
public @Nullable Cache getCache();
}
public interface Parameter {
public @Nullable String getName();
public @Nullable Integer getPosition();
}
public interface PersistenceUnitUtil extends PersistenceUtil {
public @Nullable Object getIdentifier(Object entity);
}
public interface Query {
<T> Query setParameter(Parameter<T> param, @Nullable T value);
Query setParameter(Parameter<Calendar> param, @Nullable Calendar value, TemporalType temporalType);
Query setParameter(Parameter<Date> param, @Nullable Date value, TemporalType temporalType);
Query setParameter(String name, @Nullable Object value);
Query setParameter(String name, @Nullable Calendar value, TemporalType temporalType);
Query setParameter(String name, @Nullable Date value, TemporalType temporalType);
Query setParameter(int position, @Nullable Object value);
Query setParameter(int position, @Nullable Calendar value, TemporalType temporalType);
Query setParameter(int position, @Nullable Date value, TemporalType temporalType);
<T> @Nullable T getParameterValue(Parameter<T> param);
@Nullable Object getParameterValue(String name);
@Nullable Object getParameterValue(int position);
}
public interface StoredProcedureQuery extends Query {
<T> StoredProcedureQuery setParameter(Parameter<T> param, @Nullable T value);
StoredProcedureQuery setParameter(Parameter<Calendar> param, @Nullable Calendar value, TemporalType temporalType);
StoredProcedureQuery setParameter(Parameter<Date> param, @Nullable Date value, TemporalType temporalType);
StoredProcedureQuery setParameter(String name, @Nullable Object value);
StoredProcedureQuery setParameter(String name, @Nullable Calendar value, TemporalType temporalType);
StoredProcedureQuery setParameter(String name, @Nullable Date value, TemporalType temporalType);
StoredProcedureQuery setParameter(int position, @Nullable Object value);
StoredProcedureQuery setParameter(int position, @Nullable Calendar value, TemporalType temporalType);
StoredProcedureQuery setParameter(int position, @Nullable Date value, TemporalType temporalType);
@Nullable Object getOutputParameterValue(int position);
@Nullable Object getOutputParameterValue(String parameterName);
}
public interface TypedQuery<X> extends Query {
<T> TypedQuery<X> setParameter(Parameter<T> param, @Nullable T value);
TypedQuery<X> setParameter(Parameter<Calendar> param, @Nullable Calendar value, TemporalType temporalType);
TypedQuery<X> setParameter(Parameter<Date> param, @Nullable Date value, TemporalType temporalType);
TypedQuery<X> setParameter(String name, @Nullable Object value);
TypedQuery<X> setParameter(String name, @Nullable Calendar value, TemporalType temporalType);
TypedQuery<X> setParameter(String name, @Nullable Date value, TemporalType temporalType);
TypedQuery<X> setParameter(int position, @Nullable Object value);
TypedQuery<X> setParameter(int position, @Nullable Calendar value, TemporalType temporalType);
TypedQuery<X> setParameter(int position, @Nullable Date value, TemporalType temporalType);
@Nullable Object getOutputParameterValue(int position);
@Nullable Object getOutputParameterValue(String parameterName);
}
public interface Tuple {
<X> @Nullable X get(TupleElement<X> tupleElement);
<X> @Nullable X get(String alias, Class<X> type);
@Nullable Object get(String alias);
<X> @Nullable X get(int i, Class<X> type);
@Nullable Object get(int i);
@Nullable Object[] toArray();
}
public interface TupleElement<X> {
@Nullable String getAlias();
}
package jakarta.persistence.criteria;
public interface CommonAbstractCriteria {
@Nullable Predicate getRestriction();
}
public interface AbstractQuery<T> extends CommonAbstractCriteria {
AbstractQuery<T> where(@Nullable Expression<Boolean> restriction);
AbstractQuery<T> where(@Nullable Predicate... restrictions);
AbstractQuery<T> having(@Nullable Expression<Boolean> restriction);
AbstractQuery<T> having(@Nullable Predicate... restrictions);
@Nullable Selection<T> getSelection();
@Nullable Predicate getGroupRestriction();
}
public interface CriteriaUpdate<T> extends CommonAbstractCriteria {
<Y, X extends Y> CriteriaUpdate<T> set(SingularAttribute<? super T, Y> attribute, @Nullable X value);
<Y, X extends Y> CriteriaUpdate<T> set(Path<Y> attribute, @Nullable X value);
CriteriaUpdate<T> set(String attributeName, @Nullable Object value);
}
public interface Subquery<T> extends AbstractQuery<T>, Expression<T> {
Subquery<T> where(@Nullable Expression<Boolean> restriction);
Subquery<T> where(@Nullable Predicate... restrictions);
Subquery<T> having(@Nullable Expression<Boolean> restriction);
Subquery<T> having(@Nullable Predicate... restrictions);
@Nullable Expression<T> getSelection();
}
public interface CriteriaBuilder {
public static interface SimpleCase<C,R> extends Expression<R> {
SimpleCase<C, R> when(C condition, @Nullable R result);
SimpleCase<C, R> when(Expression<? extends C> condition, @Nullable R result);
Expression<R> otherwise(@Nullable R result);
}
public static interface Case<R> extends Expression<R> {
Case<R> when(Expression<Boolean> condition, @Nullable R result);
Expression<R> otherwise(@Nullable R result);
}
}
public interface Join<Z, X> extends From<Z, X> {
Join<Z, X> on(@Nullable Expression<Boolean> restriction);
Join<Z, X> on(@Nullable Predicate... restrictions);
@Nullable Predicate getOn();
}
public interface SetJoin<Z,E> extends PluralJoin<Z, Set<E>, E> {
SetJoin<Z, E> on(@Nullable Expression<Boolean> restriction);
SetJoin<Z, E> on(@Nullable Predicate... restrictions);
}
public interface ListJoin<Z,E> extends PluralJoin<Z, List<E>, E> {
ListJoin<Z, E> on(@Nullable Expression<Boolean> restriction);
ListJoin<Z, E> on(@Nullable Predicate... restrictions);
}
public interface MapJoin<Z,K,V> extends PluralJoin<Z, Map<K,V>, V> {
MapJoin<Z,K,V> on(@Nullable Expression<Boolean> restriction);
MapJoin<Z,K,V> on(@Nullable Predicate... restrictions);
}
public interface Path<X> extends Expression<X> {
// CteRoot etc.
@Nullable Bindable<X> getModel();
@Nullable Path<?> getParentPath();
MapJoin<Z,K,V> on(@Nullable Predicate... restrictions);
}
package jakarta.persistence.metamodel;
public interface IdentifiableType<X> extends ManagedType<X> {
@Nullable IdentifiableType<? super X> getSupertype();
}
package jakarta.persistence.spi;
public interface ClassTransformer {
@Nullable byte[] transform(
@Nullable ClassLoader loader,
String className,
@Nullable Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws TransformerException;
}
public interface PersistenceProvider {
public @Nullable EntityManagerFactory createEntityManagerFactory(String emName, @Nullable Map map);
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, @Nullable Map map);
}
public interface PersistenceUnitInfo {
public @Nullable String getPersistenceProviderClassName();
public @Nullable PersistenceUnitTransactionType getTransactionType();
public @Nullable DataSource getJtaDataSource();
public @Nullable DataSource getNonJtaDataSource();
public @Nullable ClassLoader getClassLoader();
public @Nullable ClassLoader getNewTempClassLoader();
}

View File

@ -31,6 +31,8 @@ apply plugin: 'java-library'
apply plugin: 'biz.aQute.bnd.builder' apply plugin: 'biz.aQute.bnd.builder'
apply plugin: 'org.hibernate.orm.database-service' apply plugin: 'org.hibernate.orm.database-service'
apply plugin: 'org.checkerframework'
apply plugin: 'checkstyle' apply plugin: 'checkstyle'
apply plugin: 'build-dashboard' apply plugin: 'build-dashboard'
apply plugin: 'project-report' apply plugin: 'project-report'
@ -502,6 +504,9 @@ checkstyle {
// exclude generated java sources - by explicitly setting the base source dir // exclude generated java sources - by explicitly setting the base source dir
tasks.checkstyleMain.source = 'src/main/java' tasks.checkstyleMain.source = 'src/main/java'
tasks.checkstyleMain
.exclude('org/hibernate/jpamodelgen/util/NullnessUtil.java')
.exclude('org/hibernate/internal/util/NullnessUtil.java')
// define a second checkstyle task for checking non-fatal violations // define a second checkstyle task for checking non-fatal violations
task nonFatalCheckstyle(type:Checkstyle) { task nonFatalCheckstyle(type:Checkstyle) {
@ -511,6 +516,17 @@ task nonFatalCheckstyle(type:Checkstyle) {
configFile = rootProject.file( 'shared/config/checkstyle/checkstyle-non-fatal.xml' ) configFile = rootProject.file( 'shared/config/checkstyle/checkstyle-non-fatal.xml' )
} }
checkerFramework {
checkers = [
'org.checkerframework.checker.nullness.NullnessChecker'
]
extraJavacArgs = [
'-AsuppressWarnings=initialization',
"-Astubs=${project.rootDir}/checkerstubs",
'-AonlyDefs=^org\\.hibernate\\.jpamodelgen\\.'
]
}
task forbiddenApisSystemOut(type: CheckForbiddenApis, dependsOn: compileJava) { task forbiddenApisSystemOut(type: CheckForbiddenApis, dependsOn: compileJava) {
bundledSignatures += 'jdk-system-out' bundledSignatures += 'jdk-system-out'

View File

@ -0,0 +1,361 @@
/*
* Checker Framework utilities
* Copyright 2004-present by the Checker Framework developers
*
* MIT License:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.hibernate.internal.util;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.AnnotatedFor;
/**
* Utility class for the Nullness Checker.
*
* <p>To avoid the need to write the NullnessUtil class name, do:
*
* <pre>import static org.checkerframework.checker.nullness.util.NullnessUtil.castNonNull;</pre>
* <p>
* or
*
* <pre>import static org.checkerframework.checker.nullness.util.NullnessUtil.*;</pre>
*
* <p><b>Runtime Dependency</b>: If you use this class, you must distribute (or link to) {@code
* checker-qual.jar}, along with your binaries. Or, you can copy this class into your own project.
*/
@SuppressWarnings({
"nullness", // Nullness utilities are trusted regarding nullness.
"cast" // Casts look redundant if Nullness Checker is not run.
})
@AnnotatedFor("nullness")
public final class NullnessUtil {
private NullnessUtil() {
throw new AssertionError( "shouldn't be instantiated" );
}
/**
* A method that suppresses warnings from the Nullness Checker.
*
* <p>The method takes a possibly-null reference, unsafely casts it to have the @NonNull type
* qualifier, and returns it. The Nullness Checker considers both the return value, and also the
* argument, to be non-null after the method call. Therefore, the {@code castNonNull} method can
* be used either as a cast expression or as a statement. The Nullness Checker issues no warnings
* in any of the following code:
*
* <pre><code>
* // one way to use as a cast:
* {@literal @}NonNull String s = castNonNull(possiblyNull1);
*
* // another way to use as a cast:
* castNonNull(possiblyNull2).toString();
*
* // one way to use as a statement:
* castNonNull(possiblyNull3);
* possiblyNull3.toString();`
* </code></pre>
* <p>
* The {@code castNonNull} method is intended to be used in situations where the programmer
* definitively knows that a given reference is not null, but the type system is unable to make
* this deduction. It is not intended for defensive programming, in which a programmer cannot
* prove that the value is not null but wishes to have an earlier indication if it is. See the
* Checker Framework Manual for further discussion.
*
* <p>The method throws {@link AssertionError} if Java assertions are enabled and the argument is
* {@code null}. If the exception is ever thrown, then that indicates that the programmer misused
* the method by using it in a circumstance where its argument can be null.
*
* @param <T> the type of the reference
* @param ref a reference of @Nullable type, that is non-null at run time
*
* @return the argument, casted to have the type qualifier @NonNull
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T castNonNull(@Nullable T ref) {
assert ref != null : "Misuse of castNonNull: called with a null argument";
return (@NonNull T) ref;
}
/**
* Suppress warnings from the Nullness Checker, with a custom error message. See {@link
* #castNonNull(Object)} for documentation.
*
* @param <T> the type of the reference
* @param ref a reference of @Nullable type, that is non-null at run time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull
*
* @see #castNonNull(Object)
*/
public static @EnsuresNonNull("#1") <T extends @Nullable Object> @NonNull T castNonNull(
@Nullable T ref, String message) {
assert ref != null : "Misuse of castNonNull: called with a null argument: " + message;
return (@NonNull T) ref;
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [] castNonNullDeep(
T @Nullable [] arr) {
return (@NonNull T[]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [] castNonNullDeep(
T @Nullable [] arr, String message) {
return (@NonNull T[]) castNonNullArray( arr, message );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][] castNonNullDeep(
T @Nullable [] @Nullable [] arr) {
return (@NonNull T[][]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][] castNonNullDeep(
T @Nullable [] @Nullable [] arr, String message) {
return (@NonNull T[][]) castNonNullArray( arr, message );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (three levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] arr) {
return (@NonNull T[][][]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (three levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] arr, String message) {
return (@NonNull T[][][]) castNonNullArray( arr, message );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] @Nullable [] arr) {
return (@NonNull T[][][][]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (four levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] @Nullable [] arr, String message) {
return (@NonNull T[][][][]) castNonNullArray( arr, message );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (four levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] @Nullable [] @Nullable [] arr) {
return (@NonNull T[][][][][]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (five levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] @Nullable [] @Nullable [] arr, String message) {
return (@NonNull T[][][][][]) castNonNullArray( arr, message );
}
/**
* The implementation of castNonNullDeep.
*
* @param <T> the component type (five levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if there is a non-null value, or null to use uncustomized
* message
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*/
private static <T extends @Nullable Object> @NonNull T @NonNull [] castNonNullArray(
T @Nullable [] arr, @Nullable String message) {
assert arr != null
: "Misuse of castNonNullArray: called with a null array argument"
+ ( ( message == null ) ? "" : ( ": " + message ) );
for ( int i = 0; i < arr.length; ++i ) {
assert arr[i] != null
: "Misuse of castNonNull: called with a null array element"
+ ( ( message == null ) ? "" : ( ": " + message ) );
checkIfArray( arr[i], message );
}
return (@NonNull T[]) arr;
}
/**
* If the argument is an array, requires it to be non-null at all levels.
*
* @param ref a value; if an array, all of its elements, and their elements recursively, are
* non-null at run time
* @param message text to include if there is a non-null value, or null to use uncustomized
* message
*/
private static void checkIfArray(@NonNull Object ref, @Nullable String message) {
assert ref != null
: "Misuse of checkIfArray: called with a null argument"
+ ( ( message == null ) ? "" : ( ": " + message ) );
Class<?> comp = ref.getClass().getComponentType();
if ( comp != null ) {
// comp is non-null for arrays, otherwise null.
if ( comp.isPrimitive() ) {
// Nothing to do for arrays of primitive type: primitives are
// never null.
}
else {
castNonNullArray( (Object[]) ref, message );
}
}
}
}

View File

@ -27,6 +27,8 @@ import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.util.Constants; import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.TypeUtils; import org.hibernate.jpamodelgen.util.TypeUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Helper class to write the actual meta model class using the {@link javax.annotation.processing.Filer} API. * Helper class to write the actual meta model class using the {@link javax.annotation.processing.Filer} API.
* *
@ -35,6 +37,8 @@ import org.hibernate.jpamodelgen.util.TypeUtils;
*/ */
public final class ClassWriter { public final class ClassWriter {
private static final String META_MODEL_CLASS_NAME_SUFFIX = "_"; private static final String META_MODEL_CLASS_NAME_SUFFIX = "_";
// See https://github.com/typetools/checker-framework/issues/979
@SuppressWarnings( "type.argument" )
private static final ThreadLocal<SimpleDateFormat> SIMPLE_DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() { private static final ThreadLocal<SimpleDateFormat> SIMPLE_DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
@Override @Override
public SimpleDateFormat initialValue() { public SimpleDateFormat initialValue() {
@ -140,7 +144,7 @@ public final class ClassWriter {
pw.println( " {" ); pw.println( " {" );
} }
private static String findMappedSuperClass(MetaEntity entity, Context context) { private static @Nullable String findMappedSuperClass(MetaEntity entity, Context context) {
TypeMirror superClass = entity.getTypeElement().getSuperclass(); TypeMirror superClass = entity.getTypeElement().getSuperclass();
//superclass of Object is of NoType which returns some other kind //superclass of Object is of NoType which returns some other kind
while ( superClass.getKind() == TypeKind.DECLARED ) { while ( superClass.getKind() == TypeKind.DECLARED ) {

View File

@ -24,6 +24,8 @@ import org.hibernate.jpamodelgen.util.AccessType;
import org.hibernate.jpamodelgen.util.AccessTypeInformation; import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.Constants; import org.hibernate.jpamodelgen.util.Constants;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Max Andersen * @author Max Andersen
* @author Hardy Ferentschik * @author Hardy Ferentschik
@ -68,21 +70,21 @@ public final class Context {
public Context(ProcessingEnvironment pe) { public Context(ProcessingEnvironment pe) {
this.pe = pe; this.pe = pe;
if ( pe.getOptions().get( JPAMetaModelEntityProcessor.PERSISTENCE_XML_OPTION ) != null ) { String persistenceXmlOption = pe.getOptions().get( JPAMetaModelEntityProcessor.PERSISTENCE_XML_OPTION );
String tmp = pe.getOptions().get( JPAMetaModelEntityProcessor.PERSISTENCE_XML_OPTION ); if ( persistenceXmlOption != null ) {
if ( !tmp.startsWith( Constants.PATH_SEPARATOR ) ) { if ( !persistenceXmlOption.startsWith( Constants.PATH_SEPARATOR ) ) {
tmp = Constants.PATH_SEPARATOR + tmp; persistenceXmlOption = Constants.PATH_SEPARATOR + persistenceXmlOption;
} }
persistenceXmlLocation = tmp; persistenceXmlLocation = persistenceXmlOption;
} }
else { else {
persistenceXmlLocation = DEFAULT_PERSISTENCE_XML_LOCATION; persistenceXmlLocation = DEFAULT_PERSISTENCE_XML_LOCATION;
} }
if ( pe.getOptions().get( JPAMetaModelEntityProcessor.ORM_XML_OPTION ) != null ) { String ormXmlOption = pe.getOptions().get( JPAMetaModelEntityProcessor.ORM_XML_OPTION );
String tmp = pe.getOptions().get( JPAMetaModelEntityProcessor.ORM_XML_OPTION ); if ( ormXmlOption != null ) {
ormXmlFiles = new ArrayList<String>(); ormXmlFiles = new ArrayList<>();
for ( String ormFile : tmp.split( "," ) ) { for ( String ormFile : ormXmlOption.split( "," ) ) {
if ( !ormFile.startsWith( Constants.PATH_SEPARATOR ) ) { if ( !ormFile.startsWith( Constants.PATH_SEPARATOR ) ) {
ormFile = Constants.PATH_SEPARATOR + ormFile; ormFile = Constants.PATH_SEPARATOR + ormFile;
} }
@ -161,7 +163,7 @@ public final class Context {
return metaEntities.containsKey( fqcn ); return metaEntities.containsKey( fqcn );
} }
public MetaEntity getMetaEntity(String fqcn) { public @Nullable MetaEntity getMetaEntity(String fqcn) {
return metaEntities.get( fqcn ); return metaEntities.get( fqcn );
} }
@ -177,7 +179,7 @@ public final class Context {
return metaEmbeddables.containsKey( fqcn ); return metaEmbeddables.containsKey( fqcn );
} }
public MetaEntity getMetaEmbeddable(String fqcn) { public @Nullable MetaEntity getMetaEmbeddable(String fqcn) {
return metaEmbeddables.get( fqcn ); return metaEmbeddables.get( fqcn );
} }
@ -193,7 +195,7 @@ public final class Context {
accessTypeInformation.put( fqcn, info ); accessTypeInformation.put( fqcn, info );
} }
public AccessTypeInformation getAccessTypeInfo(String fqcn) { public @Nullable AccessTypeInformation getAccessTypeInfo(String fqcn) {
return accessTypeInformation.get( fqcn ); return accessTypeInformation.get( fqcn );
} }

View File

@ -36,6 +36,8 @@ import org.hibernate.jpamodelgen.util.StringUtil;
import org.hibernate.jpamodelgen.util.TypeUtils; import org.hibernate.jpamodelgen.util.TypeUtils;
import org.hibernate.jpamodelgen.xml.JpaDescriptorParser; import org.hibernate.jpamodelgen.xml.JpaDescriptorParser;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Main annotation processor. * Main annotation processor.
* *
@ -240,7 +242,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
requiresLazyMemberInitialization = true; requiresLazyMemberInitialization = true;
} }
metaEntity = new AnnotationMetaEntity( (TypeElement) element, context, requiresLazyMemberInitialization ); metaEntity = AnnotationMetaEntity.create( (TypeElement) element, context, requiresLazyMemberInitialization );
if ( alreadyExistingMetaEntity != null ) { if ( alreadyExistingMetaEntity != null ) {
metaEntity.mergeInMembers( alreadyExistingMetaEntity ); metaEntity.mergeInMembers( alreadyExistingMetaEntity );
@ -249,7 +251,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
} }
} }
private MetaEntity tryGettingExistingEntityFromContext(AnnotationMirror mirror, String fqn) { private @Nullable MetaEntity tryGettingExistingEntityFromContext(AnnotationMirror mirror, String fqn) {
MetaEntity alreadyExistingMetaEntity = null; MetaEntity alreadyExistingMetaEntity = null;
if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ENTITY ) if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ENTITY )
|| TypeUtils.isAnnotationMirrorOfType( mirror, Constants.MAPPED_SUPERCLASS )) { || TypeUtils.isAnnotationMirrorOfType( mirror, Constants.MAPPED_SUPERCLASS )) {

View File

@ -6,20 +6,24 @@
*/ */
package org.hibernate.jpamodelgen; package org.hibernate.jpamodelgen;
import org.hibernate.jpamodelgen.util.NullnessUtil;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Information about the Meta Model Generator version. * Information about the Meta Model Generator version.
* *
* @author Hardy Ferentschik * @author Hardy Ferentschik
*/ */
public final class Version { public final class Version {
private static String version; private static @Nullable String version;
private Version() { private Version() {
} }
public static String getVersionString() { public static String getVersionString() {
if ( version == null ) { if ( version == null ) {
version = Version.class.getPackage().getImplementationVersion(); version = NullnessUtil.castNonNull( Version.class.getPackage() ).getImplementationVersion();
if ( version == null ) { if ( version == null ) {
version = "[WORKING]"; version = "[WORKING]";
} }

View File

@ -28,6 +28,7 @@ import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.util.AccessType; import org.hibernate.jpamodelgen.util.AccessType;
import org.hibernate.jpamodelgen.util.AccessTypeInformation; import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.Constants; import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.NullnessUtil;
import org.hibernate.jpamodelgen.util.TypeUtils; import org.hibernate.jpamodelgen.util.TypeUtils;
/** /**
@ -66,14 +67,19 @@ public class AnnotationMetaEntity implements MetaEntity {
*/ */
private MetaEntity entityToMerge; private MetaEntity entityToMerge;
public AnnotationMetaEntity(TypeElement element, Context context, boolean lazilyInitialised) { public AnnotationMetaEntity(TypeElement element, Context context) {
this.element = element; this.element = element;
this.context = context; this.context = context;
this.members = new HashMap<String, MetaAttribute>(); this.members = new HashMap<>();
this.importContext = new ImportContextImpl( getPackageName() ); this.importContext = new ImportContextImpl( getPackageName( context, element ) );
if ( !lazilyInitialised ) {
init();
} }
public static AnnotationMetaEntity create(TypeElement element, Context context, boolean lazilyInitialised) {
final AnnotationMetaEntity annotationMetaEntity = new AnnotationMetaEntity( element, context );
if ( !lazilyInitialised ) {
annotationMetaEntity.init();
}
return annotationMetaEntity;
} }
public AccessTypeInformation getEntityAccessTypeInfo() { public AccessTypeInformation getEntityAccessTypeInfo() {
@ -93,6 +99,10 @@ public class AnnotationMetaEntity implements MetaEntity {
} }
public final String getPackageName() { public final String getPackageName() {
return getPackageName( context, element );
}
private static String getPackageName(Context context, TypeElement element) {
PackageElement packageOf = context.getElementUtils().getPackageOf( element ); PackageElement packageOf = context.getElementUtils().getPackageOf( element );
return context.getElementUtils().getName( packageOf.getQualifiedName() ).toString(); return context.getElementUtils().getName( packageOf.getQualifiedName() ).toString();
} }
@ -167,7 +177,8 @@ public class AnnotationMetaEntity implements MetaEntity {
getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." ); getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." );
TypeUtils.determineAccessTypeForHierarchy( element, context ); TypeUtils.determineAccessTypeForHierarchy( element, context );
entityAccessTypeInfo = context.getAccessTypeInfo( getQualifiedName() ); AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( getQualifiedName() );
entityAccessTypeInfo = NullnessUtil.castNonNull(accessTypeInfo);
List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() ); List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() );
addPersistentMembers( fieldsOfClass, AccessType.FIELD ); addPersistentMembers( fieldsOfClass, AccessType.FIELD );

View File

@ -25,13 +25,16 @@ import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.util.AccessType; import org.hibernate.jpamodelgen.util.AccessType;
import org.hibernate.jpamodelgen.util.AccessTypeInformation; import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.Constants; import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.NullnessUtil;
import org.hibernate.jpamodelgen.util.StringUtil; import org.hibernate.jpamodelgen.util.StringUtil;
import org.hibernate.jpamodelgen.util.TypeUtils; import org.hibernate.jpamodelgen.util.TypeUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Hardy Ferentschik * @author Hardy Ferentschik
*/ */
public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<AnnotationMetaAttribute, Element> { public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<@Nullable AnnotationMetaAttribute, Element> {
/** /**
* FQCN of the Hibernate-specific {@code @Target} annotation. * FQCN of the Hibernate-specific {@code @Target} annotation.
@ -54,12 +57,12 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
} }
@Override @Override
public AnnotationMetaAttribute visitPrimitive(PrimitiveType t, Element element) { public @Nullable AnnotationMetaAttribute visitPrimitive(PrimitiveType t, Element element) {
return new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) ); return new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) );
} }
@Override @Override
public AnnotationMetaAttribute visitArray(ArrayType t, Element element) { public @Nullable AnnotationMetaAttribute visitArray(ArrayType t, Element element) {
// METAGEN-2 - For now we handle arrays as SingularAttribute // METAGEN-2 - For now we handle arrays as SingularAttribute
// The code below is an attempt to be closer to the spec and only allow byte[], Byte[], char[] and Character[] // The code below is an attempt to be closer to the spec and only allow byte[], Byte[], char[] and Character[]
// AnnotationMetaSingleAttribute attribute = null; // AnnotationMetaSingleAttribute attribute = null;
@ -81,7 +84,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
} }
@Override @Override
public AnnotationMetaAttribute visitTypeVariable(TypeVariable t, Element element) { public @Nullable AnnotationMetaAttribute visitTypeVariable(TypeVariable t, Element element) {
// METAGEN-29 - for a type variable we use the upper bound // METAGEN-29 - for a type variable we use the upper bound
TypeMirror mirror = t.getUpperBound(); TypeMirror mirror = t.getUpperBound();
TypeMirror erasedType = context.getTypeUtils().erasure( mirror ); TypeMirror erasedType = context.getTypeUtils().erasure( mirror );
@ -91,7 +94,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
} }
@Override @Override
public AnnotationMetaAttribute visitDeclared(DeclaredType declaredType, Element element) { public @Nullable AnnotationMetaAttribute visitDeclared(DeclaredType declaredType, Element element) {
AnnotationMetaAttribute metaAttribute = null; AnnotationMetaAttribute metaAttribute = null;
TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType ); TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType );
// WARNING: .toString() is necessary here since Name equals does not compare to String // WARNING: .toString() is necessary here since Name equals does not compare to String
@ -110,7 +113,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
return metaAttribute; return metaAttribute;
} }
private AnnotationMetaAttribute createMetaCollectionAttribute(DeclaredType declaredType, Element element, String fqNameOfReturnType, String collection, String targetEntity) { private @Nullable AnnotationMetaAttribute createMetaCollectionAttribute(DeclaredType declaredType, Element element, String fqNameOfReturnType, String collection, @Nullable String targetEntity) {
if ( TypeUtils.containsAnnotation( element, Constants.ELEMENT_COLLECTION ) ) { if ( TypeUtils.containsAnnotation( element, Constants.ELEMENT_COLLECTION ) ) {
String explicitTargetEntity = getTargetEntity( element.getAnnotationMirrors() ); String explicitTargetEntity = getTargetEntity( element.getAnnotationMirrors() );
TypeMirror collectionElementType = TypeUtils.getCollectionElementType( TypeMirror collectionElementType = TypeUtils.getCollectionElementType(
@ -164,7 +167,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
} }
@Override @Override
public AnnotationMetaAttribute visitExecutable(ExecutableType t, Element p) { public @Nullable AnnotationMetaAttribute visitExecutable(ExecutableType t, Element p) {
if ( !p.getKind().equals( ElementKind.METHOD ) ) { if ( !p.getKind().equals( ElementKind.METHOD ) ) {
return null; return null;
} }
@ -196,13 +199,12 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
return returnedElement.asType().accept( basicVisitor, returnedElement ); return returnedElement.asType().accept( basicVisitor, returnedElement );
} }
private AnnotationMetaAttribute createAnnotationMetaAttributeForMap(DeclaredType declaredType, Element element, String collection, String targetEntity) { private @Nullable AnnotationMetaAttribute createAnnotationMetaAttributeForMap(DeclaredType declaredType, Element element, String collection, @Nullable String targetEntity) {
final AnnotationMirror annotationMirror = TypeUtils.getAnnotationMirror( element, Constants.MAP_KEY_CLASS );
String keyType; String keyType;
if ( TypeUtils.containsAnnotation( element, Constants.MAP_KEY_CLASS ) ) { if ( annotationMirror != null ) {
TypeMirror typeMirror = (TypeMirror) TypeUtils.getAnnotationValue( TypeMirror typeMirror = (TypeMirror) NullnessUtil.castNonNull(
TypeUtils.getAnnotationMirror( TypeUtils.getAnnotationValue( annotationMirror, TypeUtils.DEFAULT_ANNOTATION_PARAMETER_NAME )
element, Constants.MAP_KEY_CLASS
), TypeUtils.DEFAULT_ANNOTATION_PARAMETER_NAME
); );
keyType = typeMirror.toString(); keyType = typeMirror.toString();
} }
@ -218,7 +220,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
); );
} }
private String getElementType(DeclaredType declaredType, String targetEntity) { private String getElementType(DeclaredType declaredType, @Nullable String targetEntity) {
if ( targetEntity != null ) { if ( targetEntity != null ) {
return targetEntity; return targetEntity;
} }
@ -247,7 +249,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
* *
* @return target entity class name as string or {@code null} if no targetEntity is here or if equals to void * @return target entity class name as string or {@code null} if no targetEntity is here or if equals to void
*/ */
private String getTargetEntity(List<? extends AnnotationMirror> annotations) { private @Nullable String getTargetEntity(List<? extends AnnotationMirror> annotations) {
String fullyQualifiedTargetEntityName = null; String fullyQualifiedTargetEntityName = null;
for ( AnnotationMirror mirror : annotations ) { for ( AnnotationMirror mirror : annotations ) {
if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ELEMENT_COLLECTION ) ) { if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ELEMENT_COLLECTION ) ) {
@ -266,7 +268,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
return fullyQualifiedTargetEntityName; return fullyQualifiedTargetEntityName;
} }
private String getFullyQualifiedClassNameOfTargetEntity(AnnotationMirror mirror, String parameterName) { private @Nullable String getFullyQualifiedClassNameOfTargetEntity(AnnotationMirror mirror, String parameterName) {
assert mirror != null; assert mirror != null;
assert parameterName != null; assert parameterName != null;
@ -291,6 +293,7 @@ class BasicAttributeVisitor extends SimpleTypeVisitor6<Boolean, Element> {
private final Context context; private final Context context;
public BasicAttributeVisitor(Context context) { public BasicAttributeVisitor(Context context) {
super( Boolean.FALSE );
this.context = context; this.context = context;
} }

View File

@ -6,6 +6,8 @@
*/ */
package org.hibernate.jpamodelgen.util; package org.hibernate.jpamodelgen.util;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Encapsulates the access type information for a single class. * Encapsulates the access type information for a single class.
* *
@ -17,17 +19,17 @@ public class AccessTypeInformation {
/** /**
* Access type explicitly specified in xml or on an entity. * Access type explicitly specified in xml or on an entity.
*/ */
private AccessType explicitAccessType; private @Nullable AccessType explicitAccessType;
/** /**
* The default type for en entity. This type might change during the parsing/discovering process. The reason * The default type for en entity. This type might change during the parsing/discovering process. The reason
* for that is the ability to mix and match xml and annotation configuration. * for that is the ability to mix and match xml and annotation configuration.
*/ */
private AccessType defaultAccessType; private @Nullable AccessType defaultAccessType;
private static final AccessType DEFAULT_ACCESS_TYPE = AccessType.PROPERTY; private static final AccessType DEFAULT_ACCESS_TYPE = AccessType.PROPERTY;
public AccessTypeInformation(String fqcn, AccessType explicitAccessType, AccessType defaultAccessType) { public AccessTypeInformation(String fqcn, @Nullable AccessType explicitAccessType, @Nullable AccessType defaultAccessType) {
this.fqcn = fqcn; this.fqcn = fqcn;
this.explicitAccessType = explicitAccessType; this.explicitAccessType = explicitAccessType;
this.defaultAccessType = defaultAccessType; this.defaultAccessType = defaultAccessType;
@ -58,7 +60,7 @@ public class AccessTypeInformation {
this.explicitAccessType = explicitAccessType; this.explicitAccessType = explicitAccessType;
} }
public AccessType getDefaultAccessType() { public @Nullable AccessType getDefaultAccessType() {
return defaultAccessType; return defaultAccessType;
} }

View File

@ -10,6 +10,8 @@ import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Hardy Ferentschik * @author Hardy Ferentschik
*/ */
@ -26,7 +28,7 @@ public class FileTimeStampChecker implements Serializable {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if ( this == o ) { if ( this == o ) {
return true; return true;
} }

View File

@ -0,0 +1,361 @@
/*
* Checker Framework utilities
* Copyright 2004-present by the Checker Framework developers
*
* MIT License:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.hibernate.jpamodelgen.util;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.AnnotatedFor;
/**
* Utility class for the Nullness Checker.
*
* <p>To avoid the need to write the NullnessUtil class name, do:
*
* <pre>import static org.checkerframework.checker.nullness.util.NullnessUtil.castNonNull;</pre>
* <p>
* or
*
* <pre>import static org.checkerframework.checker.nullness.util.NullnessUtil.*;</pre>
*
* <p><b>Runtime Dependency</b>: If you use this class, you must distribute (or link to) {@code
* checker-qual.jar}, along with your binaries. Or, you can copy this class into your own project.
*/
@SuppressWarnings({
"nullness", // Nullness utilities are trusted regarding nullness.
"cast" // Casts look redundant if Nullness Checker is not run.
})
@AnnotatedFor("nullness")
public final class NullnessUtil {
private NullnessUtil() {
throw new AssertionError( "shouldn't be instantiated" );
}
/**
* A method that suppresses warnings from the Nullness Checker.
*
* <p>The method takes a possibly-null reference, unsafely casts it to have the @NonNull type
* qualifier, and returns it. The Nullness Checker considers both the return value, and also the
* argument, to be non-null after the method call. Therefore, the {@code castNonNull} method can
* be used either as a cast expression or as a statement. The Nullness Checker issues no warnings
* in any of the following code:
*
* <pre><code>
* // one way to use as a cast:
* {@literal @}NonNull String s = castNonNull(possiblyNull1);
*
* // another way to use as a cast:
* castNonNull(possiblyNull2).toString();
*
* // one way to use as a statement:
* castNonNull(possiblyNull3);
* possiblyNull3.toString();`
* </code></pre>
* <p>
* The {@code castNonNull} method is intended to be used in situations where the programmer
* definitively knows that a given reference is not null, but the type system is unable to make
* this deduction. It is not intended for defensive programming, in which a programmer cannot
* prove that the value is not null but wishes to have an earlier indication if it is. See the
* Checker Framework Manual for further discussion.
*
* <p>The method throws {@link AssertionError} if Java assertions are enabled and the argument is
* {@code null}. If the exception is ever thrown, then that indicates that the programmer misused
* the method by using it in a circumstance where its argument can be null.
*
* @param <T> the type of the reference
* @param ref a reference of @Nullable type, that is non-null at run time
*
* @return the argument, casted to have the type qualifier @NonNull
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T castNonNull(@Nullable T ref) {
assert ref != null : "Misuse of castNonNull: called with a null argument";
return (@NonNull T) ref;
}
/**
* Suppress warnings from the Nullness Checker, with a custom error message. See {@link
* #castNonNull(Object)} for documentation.
*
* @param <T> the type of the reference
* @param ref a reference of @Nullable type, that is non-null at run time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull
*
* @see #castNonNull(Object)
*/
public static @EnsuresNonNull("#1") <T extends @Nullable Object> @NonNull T castNonNull(
@Nullable T ref, String message) {
assert ref != null : "Misuse of castNonNull: called with a null argument: " + message;
return (@NonNull T) ref;
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [] castNonNullDeep(
T @Nullable [] arr) {
return (@NonNull T[]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [] castNonNullDeep(
T @Nullable [] arr, String message) {
return (@NonNull T[]) castNonNullArray( arr, message );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][] castNonNullDeep(
T @Nullable [] @Nullable [] arr) {
return (@NonNull T[][]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][] castNonNullDeep(
T @Nullable [] @Nullable [] arr, String message) {
return (@NonNull T[][]) castNonNullArray( arr, message );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (three levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] arr) {
return (@NonNull T[][][]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (three levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] arr, String message) {
return (@NonNull T[][][]) castNonNullArray( arr, message );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] @Nullable [] arr) {
return (@NonNull T[][][][]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (four levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] @Nullable [] arr, String message) {
return (@NonNull T[][][][]) castNonNullArray( arr, message );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (four levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] @Nullable [] @Nullable [] arr) {
return (@NonNull T[][][][][]) castNonNullArray( arr, null );
}
/**
* Like castNonNull, but whereas that method only checks and casts the reference itself, this
* traverses all levels of the argument array. The array is recursively checked to ensure that all
* elements at every array level are non-null.
*
* @param <T> the component type (five levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if this method is misused
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*
* @see #castNonNull(Object)
*/
@EnsuresNonNull("#1")
public static <T extends @Nullable Object> @NonNull T @NonNull [][][][][] castNonNullDeep(
T @Nullable [] @Nullable [] @Nullable [] @Nullable [] @Nullable [] arr, String message) {
return (@NonNull T[][][][][]) castNonNullArray( arr, message );
}
/**
* The implementation of castNonNullDeep.
*
* @param <T> the component type (five levels in) of the array
* @param arr an array all of whose elements, and their elements recursively, are non-null at run
* time
* @param message text to include if there is a non-null value, or null to use uncustomized
* message
*
* @return the argument, casted to have the type qualifier @NonNull at all levels
*/
private static <T extends @Nullable Object> @NonNull T @NonNull [] castNonNullArray(
T @Nullable [] arr, @Nullable String message) {
assert arr != null
: "Misuse of castNonNullArray: called with a null array argument"
+ ( ( message == null ) ? "" : ( ": " + message ) );
for ( int i = 0; i < arr.length; ++i ) {
assert arr[i] != null
: "Misuse of castNonNull: called with a null array element"
+ ( ( message == null ) ? "" : ( ": " + message ) );
checkIfArray( arr[i], message );
}
return (@NonNull T[]) arr;
}
/**
* If the argument is an array, requires it to be non-null at all levels.
*
* @param ref a value; if an array, all of its elements, and their elements recursively, are
* non-null at run time
* @param message text to include if there is a non-null value, or null to use uncustomized
* message
*/
private static void checkIfArray(@NonNull Object ref, @Nullable String message) {
assert ref != null
: "Misuse of checkIfArray: called with a null argument"
+ ( ( message == null ) ? "" : ( ": " + message ) );
Class<?> comp = ref.getClass().getComponentType();
if ( comp != null ) {
// comp is non-null for arrays, otherwise null.
if ( comp.isPrimitive() ) {
// Nothing to do for arrays of primitive type: primitives are
// never null.
}
else {
castNonNullArray( (Object[]) ref, message );
}
}
}
}

View File

@ -26,10 +26,12 @@ import javax.lang.model.type.UnionType;
import javax.lang.model.type.WildcardType; import javax.lang.model.type.WildcardType;
import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.SimpleTypeVisitor8;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* @author Christian Beikov * @author Christian Beikov
*/ */
public final class TypeRenderingVisitor extends SimpleTypeVisitor8<Object, Object> { public final class TypeRenderingVisitor extends SimpleTypeVisitor8<@Nullable Object, @Nullable Object> {
private final StringBuilder sb = new StringBuilder(); private final StringBuilder sb = new StringBuilder();
private final Set<TypeVariable> visitedTypeVariables = new HashSet<>(); private final Set<TypeVariable> visitedTypeVariables = new HashSet<>();
@ -66,7 +68,7 @@ public final class TypeRenderingVisitor extends SimpleTypeVisitor8<Object, Objec
} }
@Override @Override
public Object visitPrimitive(PrimitiveType t, Object o) { public @Nullable Object visitPrimitive(PrimitiveType t, @Nullable Object o) {
final String primitiveTypeName = getPrimitiveTypeName( t.getKind() ); final String primitiveTypeName = getPrimitiveTypeName( t.getKind() );
if ( primitiveTypeName != null ) { if ( primitiveTypeName != null ) {
sb.append( primitiveTypeName ); sb.append( primitiveTypeName );
@ -74,7 +76,7 @@ public final class TypeRenderingVisitor extends SimpleTypeVisitor8<Object, Objec
return null; return null;
} }
private static String getPrimitiveTypeName(TypeKind kind) { private static @Nullable String getPrimitiveTypeName(TypeKind kind) {
switch ( kind ) { switch ( kind ) {
case INT: case INT:
return "int"; return "int";
@ -99,19 +101,19 @@ public final class TypeRenderingVisitor extends SimpleTypeVisitor8<Object, Objec
} }
@Override @Override
public Object visitNull(NullType t, Object o) { public @Nullable Object visitNull(NullType t, @Nullable Object o) {
return null; return null;
} }
@Override @Override
public Object visitArray(ArrayType t, Object o) { public @Nullable Object visitArray(ArrayType t, @Nullable Object o) {
t.getComponentType().accept( this, null ); t.getComponentType().accept( this, null );
sb.append( "[]" ); sb.append( "[]" );
return t; return t;
} }
@Override @Override
public Object visitDeclared(DeclaredType t, Object o) { public @Nullable Object visitDeclared(DeclaredType t, @Nullable Object o) {
sb.append( t.asElement().toString() ); sb.append( t.asElement().toString() );
List<? extends TypeMirror> typeArguments = t.getTypeArguments(); List<? extends TypeMirror> typeArguments = t.getTypeArguments();
if ( !typeArguments.isEmpty() ) { if ( !typeArguments.isEmpty() ) {
@ -127,7 +129,7 @@ public final class TypeRenderingVisitor extends SimpleTypeVisitor8<Object, Objec
} }
@Override @Override
public Object visitTypeVariable(TypeVariable t, Object o) { public @Nullable Object visitTypeVariable(TypeVariable t, @Nullable Object o) {
final Element typeVariableElement = t.asElement(); final Element typeVariableElement = t.asElement();
if ( typeVariableElement instanceof TypeParameterElement ) { if ( typeVariableElement instanceof TypeParameterElement ) {
final TypeParameterElement typeParameter = (TypeParameterElement) typeVariableElement; final TypeParameterElement typeParameter = (TypeParameterElement) typeVariableElement;
@ -145,7 +147,7 @@ public final class TypeRenderingVisitor extends SimpleTypeVisitor8<Object, Objec
} }
@Override @Override
public Object visitWildcard(WildcardType t, Object o) { public @Nullable Object visitWildcard(WildcardType t, @Nullable Object o) {
sb.append( '?' ); sb.append( '?' );
if ( t.getExtendsBound() != null ) { if ( t.getExtendsBound() != null ) {
sb.append( " extends " ); sb.append( " extends " );
@ -159,12 +161,12 @@ public final class TypeRenderingVisitor extends SimpleTypeVisitor8<Object, Objec
} }
@Override @Override
public Object visitUnion(UnionType t, Object o) { public @Nullable Object visitUnion(UnionType t, @Nullable Object o) {
return null; return null;
} }
@Override @Override
public Object visitIntersection(IntersectionType t, Object o) { public @Nullable Object visitIntersection(IntersectionType t, @Nullable Object o) {
final List<? extends TypeMirror> bounds = t.getBounds(); final List<? extends TypeMirror> bounds = t.getBounds();
bounds.get( 0 ).accept( this, null ); bounds.get( 0 ).accept( this, null );
for ( int i = 0; i < bounds.size(); i++ ) { for ( int i = 0; i < bounds.size(); i++ ) {
@ -175,12 +177,12 @@ public final class TypeRenderingVisitor extends SimpleTypeVisitor8<Object, Objec
} }
@Override @Override
public Object visitExecutable(ExecutableType t, Object o) { public @Nullable Object visitExecutable(ExecutableType t, @Nullable Object o) {
return null; return null;
} }
@Override @Override
public Object visitNoType(NoType t, Object o) { public @Nullable Object visitNoType(NoType t, @Nullable Object o) {
return null; return null;
} }
} }

View File

@ -31,6 +31,8 @@ import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.Context; import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.MetaModelGenerationException; import org.hibernate.jpamodelgen.MetaModelGenerationException;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Utility class. * Utility class.
* *
@ -72,7 +74,7 @@ public final class TypeUtils {
public static String toTypeString(TypeMirror type) { public static String toTypeString(TypeMirror type) {
if ( type.getKind().isPrimitive() ) { if ( type.getKind().isPrimitive() ) {
return PRIMITIVE_WRAPPERS.get( type.getKind() ); return NullnessUtil.castNonNull( PRIMITIVE_WRAPPERS.get( type.getKind() ) );
} }
return TypeRenderingVisitor.toString( type ); return TypeRenderingVisitor.toString( type );
} }
@ -102,7 +104,7 @@ public final class TypeUtils {
return extractClosestRealTypeAsString( component, context ) + "[]"; return extractClosestRealTypeAsString( component, context ) + "[]";
} }
public static TypeElement getSuperclassTypeElement(TypeElement element) { public static @Nullable TypeElement getSuperclassTypeElement(TypeElement element) {
final TypeMirror superClass = element.getSuperclass(); final TypeMirror superClass = element.getSuperclass();
//superclass of Object is of NoType which returns some other kind //superclass of Object is of NoType which returns some other kind
if ( superClass.getKind() == TypeKind.DECLARED ) { if ( superClass.getKind() == TypeKind.DECLARED ) {
@ -175,7 +177,7 @@ public final class TypeUtils {
* @return the annotation mirror for the specified annotation class from the {@code Element} or {@code null} in case * @return the annotation mirror for the specified annotation class from the {@code Element} or {@code null} in case
* the {@code TypeElement} does not host the specified annotation. * the {@code TypeElement} does not host the specified annotation.
*/ */
public static AnnotationMirror getAnnotationMirror(Element element, String fqcn) { public static @Nullable AnnotationMirror getAnnotationMirror(Element element, String fqcn) {
assert element != null; assert element != null;
assert fqcn != null; assert fqcn != null;
@ -189,7 +191,7 @@ public final class TypeUtils {
return mirror; return mirror;
} }
public static Object getAnnotationValue(AnnotationMirror annotationMirror, String parameterValue) { public static @Nullable Object getAnnotationValue(AnnotationMirror annotationMirror, String parameterValue) {
assert annotationMirror != null; assert annotationMirror != null;
assert parameterValue != null; assert parameterValue != null;
@ -251,7 +253,7 @@ public final class TypeUtils {
updateEmbeddableAccessType( searchedElement, context, defaultAccessType ); updateEmbeddableAccessType( searchedElement, context, defaultAccessType );
} }
public static TypeMirror getCollectionElementType(DeclaredType t, String fqNameOfReturnedType, String explicitTargetEntityName, Context context) { public static TypeMirror getCollectionElementType(DeclaredType t, String fqNameOfReturnedType, @Nullable String explicitTargetEntityName, Context context) {
TypeMirror collectionElementType; TypeMirror collectionElementType;
if ( explicitTargetEntityName != null ) { if ( explicitTargetEntityName != null ) {
Elements elements = context.getElementUtils(); Elements elements = context.getElementUtils();
@ -300,7 +302,7 @@ public final class TypeUtils {
} }
} }
private static AccessType getDefaultAccessForHierarchy(TypeElement element, Context context) { private static @Nullable AccessType getDefaultAccessForHierarchy(TypeElement element, Context context) {
AccessType defaultAccessType = null; AccessType defaultAccessType = null;
TypeElement superClass = element; TypeElement superClass = element;
do { do {
@ -370,7 +372,7 @@ public final class TypeUtils {
* @return returns the access type of the element annotated with the id annotation. If no element is annotated * @return returns the access type of the element annotated with the id annotation. If no element is annotated
* {@code null} is returned. * {@code null} is returned.
*/ */
private static AccessType getAccessTypeInCaseElementIsRoot(TypeElement searchedElement, Context context) { private static @Nullable AccessType getAccessTypeInCaseElementIsRoot(TypeElement searchedElement, Context context) {
List<? extends Element> myMembers = searchedElement.getEnclosedElements(); List<? extends Element> myMembers = searchedElement.getEnclosedElements();
for ( Element subElement : myMembers ) { for ( Element subElement : myMembers ) {
List<? extends AnnotationMirror> entityAnnotations = List<? extends AnnotationMirror> entityAnnotations =
@ -385,7 +387,7 @@ public final class TypeUtils {
return null; return null;
} }
private static AccessType getAccessTypeOfIdAnnotation(Element element) { private static @Nullable AccessType getAccessTypeOfIdAnnotation(Element element) {
AccessType accessType = null; AccessType accessType = null;
final ElementKind kind = element.getKind(); final ElementKind kind = element.getKind();
if ( kind == ElementKind.FIELD || kind == ElementKind.METHOD ) { if ( kind == ElementKind.FIELD || kind == ElementKind.METHOD ) {
@ -399,13 +401,12 @@ public final class TypeUtils {
|| TypeUtils.isAnnotationMirrorOfType( annotationMirror, Constants.EMBEDDED_ID ); || TypeUtils.isAnnotationMirrorOfType( annotationMirror, Constants.EMBEDDED_ID );
} }
public static AccessType determineAnnotationSpecifiedAccessType(Element element) { public static @Nullable AccessType determineAnnotationSpecifiedAccessType(Element element) {
final AnnotationMirror accessAnnotationMirror = TypeUtils.getAnnotationMirror( element, Constants.ACCESS ); final AnnotationMirror accessAnnotationMirror = TypeUtils.getAnnotationMirror( element, Constants.ACCESS );
AccessType forcedAccessType = null; AccessType forcedAccessType = null;
if ( accessAnnotationMirror != null ) { if ( accessAnnotationMirror != null ) {
Element accessElement = (Element) TypeUtils.getAnnotationValue( Element accessElement = (Element) NullnessUtil.castNonNull(
accessAnnotationMirror, TypeUtils.getAnnotationValue( accessAnnotationMirror, DEFAULT_ANNOTATION_PARAMETER_NAME )
DEFAULT_ANNOTATION_PARAMETER_NAME
); );
if ( accessElement.getKind().equals( ElementKind.ENUM_CONSTANT ) ) { if ( accessElement.getKind().equals( ElementKind.ENUM_CONSTANT ) ) {
if ( accessElement.getSimpleName().toString().equals( AccessType.PROPERTY.toString() ) ) { if ( accessElement.getSimpleName().toString().equals( AccessType.PROPERTY.toString() ) ) {
@ -436,7 +437,7 @@ public final class TypeUtils {
return extractClosestRealTypeAsString( typeArguments.get( 0 ), context ); return extractClosestRealTypeAsString( typeArguments.get( 0 ), context );
} }
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor6<String, Element> { static class EmbeddedAttributeVisitor extends SimpleTypeVisitor6<@Nullable String, Element> {
private Context context; private Context context;
EmbeddedAttributeVisitor(Context context) { EmbeddedAttributeVisitor(Context context) {
@ -444,7 +445,7 @@ public final class TypeUtils {
} }
@Override @Override
public String visitDeclared(DeclaredType declaredType, Element element) { public @Nullable String visitDeclared(DeclaredType declaredType, Element element) {
TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType ); TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType );
String fqNameOfReturnType = null; String fqNameOfReturnType = null;
if ( containsAnnotation( returnedElement, Constants.EMBEDDABLE ) ) { if ( containsAnnotation( returnedElement, Constants.EMBEDDABLE ) ) {
@ -454,7 +455,7 @@ public final class TypeUtils {
} }
@Override @Override
public String visitExecutable(ExecutableType t, Element p) { public @Nullable String visitExecutable(ExecutableType t, Element p) {
if ( !p.getKind().equals( ElementKind.METHOD ) ) { if ( !p.getKind().equals( ElementKind.METHOD ) ) {
return null; return null;
} }

View File

@ -131,10 +131,10 @@ public class JpaNamespaceTransformingEventReader extends EventReaderDelegate {
} }
private List<Namespace> updateElementNamespaces(StartElement startElement) { private List<Namespace> updateElementNamespaces(StartElement startElement) {
List<Namespace> newNamespaceList = new ArrayList<Namespace>(); List<Namespace> newNamespaceList = new ArrayList<>();
Iterator<?> existingNamespaceIterator = startElement.getNamespaces(); Iterator<Namespace> existingNamespaceIterator = startElement.getNamespaces();
while ( existingNamespaceIterator.hasNext() ) { while ( existingNamespaceIterator.hasNext() ) {
Namespace namespace = (Namespace) existingNamespaceIterator.next(); Namespace namespace = existingNamespaceIterator.next();
if ( NAMESPACE_MAPPING.containsKey( namespace.getNamespaceURI() ) ) { if ( NAMESPACE_MAPPING.containsKey( namespace.getNamespaceURI() ) ) {
newNamespaceList.add( xmlEventFactory.createNamespace( EMPTY_PREFIX, currentDocumentNamespaceUri ) ); newNamespaceList.add( xmlEventFactory.createNamespace( EMPTY_PREFIX, currentDocumentNamespaceUri ) );
} }
@ -153,10 +153,10 @@ public class JpaNamespaceTransformingEventReader extends EventReaderDelegate {
private List<Attribute> updateElementAttributes(StartElement startElement) { private List<Attribute> updateElementAttributes(StartElement startElement) {
// adjust the version attribute // adjust the version attribute
List<Attribute> newElementAttributeList = new ArrayList<Attribute>(); List<Attribute> newElementAttributeList = new ArrayList<>();
Iterator<?> existingAttributesIterator = startElement.getAttributes(); Iterator<Attribute> existingAttributesIterator = startElement.getAttributes();
while ( existingAttributesIterator.hasNext() ) { while ( existingAttributesIterator.hasNext() ) {
Attribute attribute = (Attribute) existingAttributesIterator.next(); Attribute attribute = existingAttributesIterator.next();
if ( VERSION_ATTRIBUTE_NAME.equals( attribute.getName().getLocalPart() ) ) { if ( VERSION_ATTRIBUTE_NAME.equals( attribute.getName().getLocalPart() ) ) {
if ( currentDocumentNamespaceUri.equals( DEFAULT_PERSISTENCE_NAMESPACE ) ) { if ( currentDocumentNamespaceUri.equals( DEFAULT_PERSISTENCE_NAMESPACE ) ) {
if ( !DEFAULT_PERSISTENCE_VERSION.equals( attribute.getName().getPrefix() ) ) { if ( !DEFAULT_PERSISTENCE_VERSION.equals( attribute.getName().getPrefix() ) ) {

View File

@ -24,8 +24,10 @@ import javax.xml.validation.SchemaFactory;
import org.hibernate.jpamodelgen.Context; import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.util.Constants; import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.NullnessUtil;
import org.hibernate.jpamodelgen.xml.jaxb.ObjectFactory; import org.hibernate.jpamodelgen.xml.jaxb.ObjectFactory;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
/** /**
@ -48,7 +50,7 @@ public class XmlParserHelper {
private static final XMLInputFactory XML_INPUT_FACTORY = XMLInputFactory.newInstance(); private static final XMLInputFactory XML_INPUT_FACTORY = XMLInputFactory.newInstance();
private static final ConcurrentMap<String, Schema> SCHEMA_CACHE = new ConcurrentHashMap<String, Schema>( private static final ConcurrentMap<String, Schema> SCHEMA_CACHE = new ConcurrentHashMap<>(
NUMBER_OF_SCHEMAS NUMBER_OF_SCHEMAS
); );
@ -66,7 +68,7 @@ public class XmlParserHelper {
* *
* @return an input stream for the specified resource or {@code null} in case resource cannot be loaded * @return an input stream for the specified resource or {@code null} in case resource cannot be loaded
*/ */
public InputStream getInputStreamForResource(String resource) { public @Nullable InputStream getInputStreamForResource(String resource) {
// METAGEN-75 // METAGEN-75
if ( !resource.startsWith( RESOURCE_PATH_SEPARATOR ) ) { if ( !resource.startsWith( RESOURCE_PATH_SEPARATOR ) ) {
resource = RESOURCE_PATH_SEPARATOR + resource; resource = RESOURCE_PATH_SEPARATOR + resource;
@ -160,19 +162,17 @@ public class XmlParserHelper {
} }
private Schema loadSchema(String schemaName) throws XmlParsingException { private Schema loadSchema(String schemaName) throws XmlParsingException {
Schema schema = null; URL schemaUrl = NullnessUtil.castNonNull( this.getClass().getClassLoader() ).getResource( schemaName );
URL schemaUrl = this.getClass().getClassLoader().getResource( schemaName );
if ( schemaUrl == null ) { if ( schemaUrl == null ) {
return schema; throw new IllegalArgumentException( "Couldn't find schema on classpath: " + schemaName );
} }
SchemaFactory sf = SchemaFactory.newInstance( javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI ); SchemaFactory sf = SchemaFactory.newInstance( javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI );
try { try {
schema = sf.newSchema( schemaUrl ); return sf.newSchema( schemaUrl );
} }
catch ( SAXException e ) { catch ( SAXException e ) {
throw new XmlParsingException( "Unable to create schema for " + schemaName + ": " + e.getMessage(), e ); throw new XmlParsingException( "Unable to create schema for " + schemaName + ": " + e.getMessage(), e );
} }
return schema;
} }
} }

View File

@ -38,6 +38,8 @@ import org.hibernate.jpamodelgen.xml.jaxb.Persistence;
import org.hibernate.jpamodelgen.xml.jaxb.PersistenceUnitDefaults; import org.hibernate.jpamodelgen.xml.jaxb.PersistenceUnitDefaults;
import org.hibernate.jpamodelgen.xml.jaxb.PersistenceUnitMetadata; import org.hibernate.jpamodelgen.xml.jaxb.PersistenceUnitMetadata;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Parser for JPA XML descriptors (persistence.xml and referenced mapping files). * Parser for JPA XML descriptors (persistence.xml and referenced mapping files).
* *
@ -105,7 +107,7 @@ public class JpaDescriptorParser {
return mappingFileNames; return mappingFileNames;
} }
private Persistence getPersistence() { private @Nullable Persistence getPersistence() {
Persistence persistence = null; Persistence persistence = null;
String persistenceXmlLocation = context.getPersistenceXmlLocation(); String persistenceXmlLocation = context.getPersistenceXmlLocation();
final InputStream stream = xmlParserHelper.getInputStreamForResource( persistenceXmlLocation ); final InputStream stream = xmlParserHelper.getInputStreamForResource( persistenceXmlLocation );
@ -249,7 +251,7 @@ public class JpaDescriptorParser {
continue; continue;
} }
XmlMetaEntity metaEntity = new XmlMetaEntity( XmlMetaEntity metaEntity = XmlMetaEntity.create(
entity, defaultPackageName, getXmlMappedType( fqcn ), context entity, defaultPackageName, getXmlMappedType( fqcn ), context
); );
if ( context.containsMetaEntity( fqcn ) ) { if ( context.containsMetaEntity( fqcn ) ) {
@ -333,8 +335,9 @@ public class JpaDescriptorParser {
private AccessType determineEntityAccessType(EntityMappings mappings) { private AccessType determineEntityAccessType(EntityMappings mappings) {
AccessType accessType = context.getPersistenceUnitDefaultAccessType(); AccessType accessType = context.getPersistenceUnitDefaultAccessType();
if ( mappings.getAccess() != null ) { final org.hibernate.jpamodelgen.xml.jaxb.AccessType mappingsAccess = mappings.getAccess();
accessType = mapXmlAccessTypeToJpaAccessType( mappings.getAccess() ); if ( mappingsAccess != null ) {
accessType = mapXmlAccessTypeToJpaAccessType( mappingsAccess );
} }
return accessType; return accessType;
} }
@ -456,15 +459,11 @@ public class JpaDescriptorParser {
private AccessType mapXmlAccessTypeToJpaAccessType(org.hibernate.jpamodelgen.xml.jaxb.AccessType xmlAccessType) { private AccessType mapXmlAccessTypeToJpaAccessType(org.hibernate.jpamodelgen.xml.jaxb.AccessType xmlAccessType) {
switch ( xmlAccessType ) { switch ( xmlAccessType ) {
case FIELD: { case FIELD:
return AccessType.FIELD; return AccessType.FIELD;
} case PROPERTY:
case PROPERTY: {
return AccessType.PROPERTY; return AccessType.PROPERTY;
} }
default: { throw new IllegalArgumentException( "Unknown access type: " + xmlAccessType );
}
}
return null;
} }
} }

View File

@ -26,6 +26,7 @@ import org.hibernate.jpamodelgen.model.ImportContext;
import org.hibernate.jpamodelgen.model.MetaAttribute; import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.MetaEntity; import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.util.AccessTypeInformation; import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.NullnessUtil;
import org.hibernate.jpamodelgen.util.StringUtil; import org.hibernate.jpamodelgen.util.StringUtil;
import org.hibernate.jpamodelgen.util.TypeUtils; import org.hibernate.jpamodelgen.util.TypeUtils;
import org.hibernate.jpamodelgen.xml.jaxb.Attributes; import org.hibernate.jpamodelgen.xml.jaxb.Attributes;
@ -44,6 +45,8 @@ import org.hibernate.jpamodelgen.xml.jaxb.MappedSuperclass;
import org.hibernate.jpamodelgen.xml.jaxb.OneToMany; import org.hibernate.jpamodelgen.xml.jaxb.OneToMany;
import org.hibernate.jpamodelgen.xml.jaxb.OneToOne; import org.hibernate.jpamodelgen.xml.jaxb.OneToOne;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Collects XML-based meta information about an annotated type (entity, embeddable or mapped superclass). * Collects XML-based meta information about an annotated type (entity, embeddable or mapped superclass).
* *
@ -64,13 +67,13 @@ public class XmlMetaEntity implements MetaEntity {
private final String packageName; private final String packageName;
private final String defaultPackageName; private final String defaultPackageName;
private final ImportContext importContext; private final ImportContext importContext;
private final List<MetaAttribute> members = new ArrayList<MetaAttribute>(); private final List<MetaAttribute> members = new ArrayList<>();
private final TypeElement element; private final TypeElement element;
private final Context context; private final Context context;
private final boolean isMetaComplete; private final boolean isMetaComplete;
private Attributes attributes; private @Nullable Attributes attributes;
private EmbeddableAttributes embeddableAttributes; private @Nullable EmbeddableAttributes embeddableAttributes;
private AccessTypeInformation accessTypeInfo; private AccessTypeInformation accessTypeInfo;
/** /**
@ -90,8 +93,13 @@ public class XmlMetaEntity implements MetaEntity {
this( ormEntity.getClazz(), defaultPackageName, element, context, ormEntity.isMetadataComplete() ); this( ormEntity.getClazz(), defaultPackageName, element, context, ormEntity.isMetadataComplete() );
this.attributes = ormEntity.getAttributes(); this.attributes = ormEntity.getAttributes();
this.embeddableAttributes = null; this.embeddableAttributes = null;
}
static XmlMetaEntity create(Entity ormEntity, String defaultPackageName, TypeElement element, Context context) {
XmlMetaEntity entity = new XmlMetaEntity( ormEntity, defaultPackageName, element, context );
// entities can be directly initialised // entities can be directly initialised
init(); entity.init();
return entity;
} }
XmlMetaEntity(MappedSuperclass mappedSuperclass, String defaultPackageName, TypeElement element, Context context) { XmlMetaEntity(MappedSuperclass mappedSuperclass, String defaultPackageName, TypeElement element, Context context) {
@ -125,15 +133,15 @@ public class XmlMetaEntity implements MetaEntity {
this.clazzName = className; this.clazzName = className;
this.packageName = pkg; this.packageName = pkg;
this.context = context; this.context = context;
this.importContext = new ImportContextImpl( getPackageName() ); this.importContext = new ImportContextImpl( pkg );
this.element = element; this.element = element;
this.isMetaComplete = initIsMetaComplete( metaComplete ); this.isMetaComplete = initIsMetaComplete( context, metaComplete );
} }
private final void init() { private final void init() {
context.logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." ); context.logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." );
this.accessTypeInfo = context.getAccessTypeInfo( getQualifiedName() ); this.accessTypeInfo = NullnessUtil.castNonNull( context.getAccessTypeInfo( getQualifiedName() ) );
if ( attributes != null ) { if ( attributes != null ) {
parseAttributes( attributes ); parseAttributes( attributes );
} }
@ -197,11 +205,11 @@ public class XmlMetaEntity implements MetaEntity {
return sb.toString(); return sb.toString();
} }
private boolean initIsMetaComplete(Boolean metadataComplete) { private static boolean initIsMetaComplete(Context context, Boolean metadataComplete) {
return context.isFullyXmlConfigured() || Boolean.TRUE.equals( metadataComplete ); return context.isFullyXmlConfigured() || Boolean.TRUE.equals( metadataComplete );
} }
private String[] getCollectionTypes(String propertyName, String explicitTargetEntity, String explicitMapKeyClass, ElementKind expectedElementKind) { private @Nullable String @Nullable[] getCollectionTypes(String propertyName, String explicitTargetEntity, @Nullable String explicitMapKeyClass, ElementKind expectedElementKind) {
for ( Element elem : element.getEnclosedElements() ) { for ( Element elem : element.getEnclosedElements() ) {
if ( !expectedElementKind.equals( elem.getKind() ) ) { if ( !expectedElementKind.equals( elem.getKind() ) ) {
continue; continue;
@ -226,7 +234,7 @@ public class XmlMetaEntity implements MetaEntity {
return null; return null;
} }
private DeclaredType determineDeclaredType(Element elem) { private @Nullable DeclaredType determineDeclaredType(Element elem) {
DeclaredType type = null; DeclaredType type = null;
if ( elem.asType() instanceof DeclaredType ) { if ( elem.asType() instanceof DeclaredType ) {
type = ( (DeclaredType) elem.asType() ); type = ( (DeclaredType) elem.asType() );
@ -240,17 +248,16 @@ public class XmlMetaEntity implements MetaEntity {
return type; return type;
} }
private String[] determineTypes(String propertyName, String explicitTargetEntity, String explicitMapKeyClass, DeclaredType type) { private @Nullable String[] determineTypes(String propertyName, String explicitTargetEntity, @Nullable String explicitMapKeyClass, DeclaredType type) {
String[] types = new String[3]; @Nullable String[] types = new String[3];
determineTargetType( type, propertyName, explicitTargetEntity, types ); determineTargetType( type, propertyName, explicitTargetEntity, types );
determineCollectionType( type, types ); if ( determineCollectionType( type, types ).equals( "jakarta.persistence.metamodel.MapAttribute" ) ) {
if ( types[1].equals( "jakarta.persistence.metamodel.MapAttribute" ) ) {
determineMapType( type, explicitMapKeyClass, types ); determineMapType( type, explicitMapKeyClass, types );
} }
return types; return types;
} }
private void determineMapType(DeclaredType type, String explicitMapKeyClass, String[] types) { private void determineMapType(DeclaredType type, @Nullable String explicitMapKeyClass, @Nullable String[] types) {
if ( explicitMapKeyClass != null ) { if ( explicitMapKeyClass != null ) {
types[2] = explicitMapKeyClass; types[2] = explicitMapKeyClass;
} }
@ -259,11 +266,11 @@ public class XmlMetaEntity implements MetaEntity {
} }
} }
private void determineCollectionType(DeclaredType type, String[] types) { private String determineCollectionType(DeclaredType type, @Nullable String[] types) {
types[1] = COLLECTIONS.get( type.asElement().toString() ); return NullnessUtil.castNonNull( types[1] = COLLECTIONS.get( type.asElement().toString() ) );
} }
private void determineTargetType(DeclaredType type, String propertyName, String explicitTargetEntity, String[] types) { private void determineTargetType(DeclaredType type, String propertyName, String explicitTargetEntity, @Nullable String[] types) {
List<? extends TypeMirror> typeArguments = type.getTypeArguments(); List<? extends TypeMirror> typeArguments = type.getTypeArguments();
if ( typeArguments.size() == 0 && explicitTargetEntity == null ) { if ( typeArguments.size() == 0 && explicitTargetEntity == null ) {
@ -288,7 +295,7 @@ public class XmlMetaEntity implements MetaEntity {
* @return The entity type for this property or {@code null} if the property with the name and the matching access * @return The entity type for this property or {@code null} if the property with the name and the matching access
* type does not exist. * type does not exist.
*/ */
private String getType(String propertyName, String explicitTargetEntity, ElementKind expectedElementKind) { private @Nullable String getType(String propertyName, @Nullable String explicitTargetEntity, ElementKind expectedElementKind) {
for ( Element elem : element.getEnclosedElements() ) { for ( Element elem : element.getEnclosedElements() ) {
if ( !expectedElementKind.equals( elem.getKind() ) ) { if ( !expectedElementKind.equals( elem.getKind() ) ) {
continue; continue;
@ -413,7 +420,7 @@ public class XmlMetaEntity implements MetaEntity {
} }
} }
private void parseEmbeddableAttributes(EmbeddableAttributes attributes) { private void parseEmbeddableAttributes(@Nullable EmbeddableAttributes attributes) {
if ( attributes == null ) { if ( attributes == null ) {
return; return;
} }
@ -449,7 +456,7 @@ public class XmlMetaEntity implements MetaEntity {
} }
private boolean parseElementCollection(ElementCollection collection) { private boolean parseElementCollection(ElementCollection collection) {
String[] types; @Nullable String @Nullable[] types;
XmlMetaCollection metaCollection; XmlMetaCollection metaCollection;
ElementKind elementKind = getElementKind( collection.getAccess() ); ElementKind elementKind = getElementKind( collection.getAccess() );
String explicitTargetClass = determineExplicitTargetEntity( collection.getTargetClass() ); String explicitTargetClass = determineExplicitTargetEntity( collection.getTargetClass() );
@ -464,11 +471,14 @@ public class XmlMetaEntity implements MetaEntity {
return true; return true;
} }
if ( types != null ) { if ( types != null ) {
if ( types[2] == null ) { final String type = NullnessUtil.castNonNull( types[0] );
metaCollection = new XmlMetaCollection( this, collection.getName(), types[0], types[1] ); final String collectionType = NullnessUtil.castNonNull( types[1] );
final String keyType = types[2];
if ( keyType == null ) {
metaCollection = new XmlMetaCollection( this, collection.getName(), type, collectionType );
} }
else { else {
metaCollection = new XmlMetaMap( this, collection.getName(), types[0], types[1], types[2] ); metaCollection = new XmlMetaMap( this, collection.getName(), type, collectionType, keyType );
} }
members.add( metaCollection ); members.add( metaCollection );
} }
@ -495,7 +505,7 @@ public class XmlMetaEntity implements MetaEntity {
return explicitTargetClass; return explicitTargetClass;
} }
private String determineExplicitMapKeyClass(MapKeyClass mapKeyClass) { private @Nullable String determineExplicitMapKeyClass(MapKeyClass mapKeyClass) {
String explicitMapKey = null; String explicitMapKey = null;
if ( mapKeyClass != null ) { if ( mapKeyClass != null ) {
explicitMapKey = StringUtil.determineFullyQualifiedClassName( defaultPackageName, mapKeyClass.getClazz() ); explicitMapKey = StringUtil.determineFullyQualifiedClassName( defaultPackageName, mapKeyClass.getClazz() );
@ -504,7 +514,7 @@ public class XmlMetaEntity implements MetaEntity {
} }
private boolean parseOneToMany(OneToMany oneToMany) { private boolean parseOneToMany(OneToMany oneToMany) {
String[] types; @Nullable String @Nullable [] types;
XmlMetaCollection metaCollection; XmlMetaCollection metaCollection;
ElementKind elementKind = getElementKind( oneToMany.getAccess() ); ElementKind elementKind = getElementKind( oneToMany.getAccess() );
String explicitTargetClass = determineExplicitTargetEntity( oneToMany.getTargetEntity() ); String explicitTargetClass = determineExplicitTargetEntity( oneToMany.getTargetEntity() );
@ -517,11 +527,14 @@ public class XmlMetaEntity implements MetaEntity {
return true; return true;
} }
if ( types != null ) { if ( types != null ) {
if ( types[2] == null ) { final String type = NullnessUtil.castNonNull( types[0] );
metaCollection = new XmlMetaCollection( this, oneToMany.getName(), types[0], types[1] ); final String collectionType = NullnessUtil.castNonNull( types[1] );
final String keyType = types[2];
if ( keyType == null ) {
metaCollection = new XmlMetaCollection( this, oneToMany.getName(), type, collectionType );
} }
else { else {
metaCollection = new XmlMetaMap( this, oneToMany.getName(), types[0], types[1], types[2] ); metaCollection = new XmlMetaMap( this, oneToMany.getName(), type, collectionType, keyType );
} }
members.add( metaCollection ); members.add( metaCollection );
} }
@ -529,7 +542,7 @@ public class XmlMetaEntity implements MetaEntity {
} }
private boolean parseManyToMany(ManyToMany manyToMany) { private boolean parseManyToMany(ManyToMany manyToMany) {
String[] types; @Nullable String @Nullable [] types;
XmlMetaCollection metaCollection; XmlMetaCollection metaCollection;
ElementKind elementKind = getElementKind( manyToMany.getAccess() ); ElementKind elementKind = getElementKind( manyToMany.getAccess() );
String explicitTargetClass = determineExplicitTargetEntity( manyToMany.getTargetEntity() ); String explicitTargetClass = determineExplicitTargetEntity( manyToMany.getTargetEntity() );
@ -544,11 +557,14 @@ public class XmlMetaEntity implements MetaEntity {
return true; return true;
} }
if ( types != null ) { if ( types != null ) {
if ( types[2] == null ) { final String type = NullnessUtil.castNonNull( types[0] );
metaCollection = new XmlMetaCollection( this, manyToMany.getName(), types[0], types[1] ); final String collectionType = NullnessUtil.castNonNull( types[1] );
final String keyType = types[2];
if ( keyType == null ) {
metaCollection = new XmlMetaCollection( this, manyToMany.getName(), type, collectionType );
} }
else { else {
metaCollection = new XmlMetaMap( this, manyToMany.getName(), types[0], types[1], types[2] ); metaCollection = new XmlMetaMap( this, manyToMany.getName(), type, collectionType, keyType );
} }
members.add( metaCollection ); members.add( metaCollection );
} }