
com.github.victools.jsonschema.generator.MemberScope Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsonschema-generator Show documentation
Show all versions of jsonschema-generator Show documentation
Java JSON Schema Generator – creating a JSON Schema (Draft 6, Draft 7 or Draft 2019-09) from your Java classes
/*
* Copyright 2019 VicTools.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.victools.jsonschema.generator;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.ResolvedTypeWithMembers;
import com.fasterxml.classmate.members.ResolvedField;
import com.fasterxml.classmate.members.ResolvedMember;
import com.fasterxml.classmate.members.ResolvedMethod;
import com.github.victools.jsonschema.generator.impl.LazyValue;
import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
/**
* Representation of a single introspected field or method.
*
* @param type of member in scope (i.e. {@link ResolvedField} or {@link ResolvedMethod}).
* @param type of java/reflection member in scope (i.e. {@link java.lang.reflect.Field Field} or {@link java.lang.reflect.Method Method}
*/
public abstract class MemberScope, T extends Member> extends TypeScope {
private final M member;
private final DeclarationDetails declarationDetails;
private final ResolvedType overriddenType;
private final String overriddenName;
private Integer fakeContainerItemIndex;
private final LazyValue schemaPropertyName = new LazyValue<>(this::doGetSchemaPropertyName);
/**
* Constructor.
*
* @param member targeted field or method
* @param declarationDetails basic details regarding the declaration context
* @param overrideDetails augmenting details (e.g., overridden type, name, or container item index)
* @param context the overall type resolution context
*/
protected MemberScope(M member, DeclarationDetails declarationDetails, OverrideDetails overrideDetails, TypeContext context) {
super(Optional.ofNullable(OverrideDetails.getOverriddenType(overrideDetails)).orElseGet(member::getType), context);
this.member = member;
this.declarationDetails = declarationDetails;
this.overriddenType = OverrideDetails.getOverriddenType(overrideDetails);
this.overriddenName = OverrideDetails.getOverriddenName(overrideDetails);
this.fakeContainerItemIndex = OverrideDetails.getFakeContainerItemIndex(overrideDetails);
}
/**
* Getter for the {@code fakeContainerItemIndex} (may be null).
*
* @return index of the container item on the generic field/method scope's declared type (e.g., in case of a List, it is 0)
* @see #isFakeContainerItemScope()
*/
protected Integer getFakeContainerItemIndex() {
return this.fakeContainerItemIndex;
}
/**
* Create another instance for this field or method and context, but overriding the declared field/method return type with the given one.
*
* @param overriddenType alternative type for this field or method return value (overriding the declared type)
* @return new instance with the given type override
* @see #getDeclaredType()
* @see #getOverriddenType()
*/
public abstract MemberScope withOverriddenType(ResolvedType overriddenType);
/**
* Create another instance for this field or method and context, representing its field/method return type's container item type.
*
* @return new instance with the container item type as override (or an identical copy if this is not a container type)
* @see #withOverriddenType(ResolvedType)
* @see #getContainerItemType()
* @see #isFakeContainerItemScope()
*/
public MemberScope asFakeContainerItemScope() {
if (!this.isContainerType()) {
return this.withOverriddenType(this.getOverriddenType());
}
MemberScope result = this.withOverriddenType(this.getContainerItemType());
result.fakeContainerItemIndex = 0;
return result;
}
/**
* Create another instance for this field or method and context, representing its field/method return type's container item type.
*
* @param referenceType the generic "container type" from which to extract the "container item type"
* @param containerItemIndex index of the container item on the generic field/method scope's declared type (e.g., in case of a List, it is 0)
* @return new instance with the container item type as override (or an identical copy if this is not a container type)
* @see #withOverriddenType(ResolvedType)
* @see #getContainerItemType()
* @see #isFakeContainerItemScope()
*/
public MemberScope asFakeContainerItemScope(Class> referenceType, int containerItemIndex) {
MemberScope result = this.withOverriddenType(this.getTypeParameterFor(referenceType, containerItemIndex));
result.fakeContainerItemIndex = containerItemIndex;
return result;
}
/**
* Create another instance for this field or method and context, but overriding the declared field/method name with the given one.
*
* @param overriddenName alternative name for this field or method
* @return new instance with the given name override
* @see #getDeclaredType()
* @see #getOverriddenType()
*/
public abstract MemberScope withOverriddenName(String overriddenName);
/**
* Getter for the represented field or method.
*
* @return represented field or method
*/
public M getMember() {
return this.member;
}
/**
* Getter for additional declaration context information.
*
* @return wrapper for the schema target type and declaring type's field and methods
*/
public DeclarationDetails getDeclarationDetails() {
return this.declarationDetails;
}
/**
* Getter for the collection of the member's declaring type's (other) fields and methods.
*
* @return declaring type's fields and methods
*/
public ResolvedTypeWithMembers getDeclaringTypeMembers() {
return this.declarationDetails.getDeclaringTypeMembers();
}
/**
* Returns the type declared as the field's or method return value's type.
*
* @return declared type
* @see #getType()
*/
public ResolvedType getDeclaredType() {
return this.member.getType();
}
/**
* Returns the overridden type of the field or method's return value.
*
* @return overridden type (or {@code null} if no override applies)
* @see #getType()
*/
public ResolvedType getOverriddenType() {
return this.overriddenType;
}
/**
* Check whether this field/method scope represents only the container item type of the actual field/method.
*
* @return whether this is not the actual field/method but a representation of its container item type
*/
public boolean isFakeContainerItemScope() {
return this.fakeContainerItemIndex != null;
}
/**
* Returns the member's name as specified in the declaring class.
*
* @return declared method/field name
* @see #getName()
*/
public String getDeclaredName() {
return this.member.getName();
}
/**
* Returns the member's overridden name.
*
* @return overridden name (or {@code null} if no override applies)
* @see #getName()
*/
public String getOverriddenName() {
return this.overriddenName;
}
/**
* Returns name of this member.
*
* @return method/field name
* @see #getDeclaredName()
* @see #getOverriddenName()
*/
public String getName() {
return Optional.ofNullable(this.getOverriddenName())
.orElseGet(this::getDeclaredName);
}
/* ===================================== *
* Delegators to wrapped resolved member *
* ===================================== */
/**
* Returns the member's declaring type.
*
* @return declaring type
*/
public final ResolvedType getDeclaringType() {
return this.member.getDeclaringType();
}
/**
* Returns the JDK object that represents member.
*
* @return raw member
*/
public T getRawMember() {
return this.member.getRawMember();
}
/**
* Indicates whether the member has the {@code static} keyword.
*
* @return whether member is static
*/
public boolean isStatic() {
return this.member.isStatic();
}
/**
* Indicates whether the member has the {@code final} keyword.
*
* @return whether member is final
*/
public boolean isFinal() {
return this.member.isFinal();
}
/**
* Indicates whether the member is of {@code private} visibility.
*
* @return whether member is private
*/
public boolean isPrivate() {
return this.member.isPrivate();
}
/**
* Indicates whether the member is of {@code protected} visibility.
*
* @return whether member is protected
*/
public boolean isProtected() {
return this.member.isProtected();
}
/**
* Indicates whether the member is of {@code public} visibility.
*
* @return whether member is public
*/
public boolean isPublic() {
return this.member.isPublic();
}
/**
* Return the annotation of the given type on the member, if such an annotation is present.
*
* Using this method is equivalent to invoking {@link #getAnnotation(Class, Predicate)} with the second parameter always returning {@code false}.
*
* @param type of annotation to look-up
* @param annotationClass annotation class to look up instance on member for
* @return annotation instance (or {@code null} if no annotation of the given type is present
*/
public A getAnnotation(Class annotationClass) {
return this.getAnnotation(annotationClass, TypeContext.IGNORE_ANNOTATIONS_ON_ANNOTATIONS);
}
/**
* Return the annotation of the given type on the member, if such an annotation is present.
*
* Additionally, also consider annotations on annotations, if the given predicate indicates another annotation as eligible for holding the target.
*
* @param type of annotation to look-up
* @param annotationClass annotation class to look up instance on member for
* @param considerOtherAnnotation check whether some other annotation should also be checked for holding an instance of the target annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present
*
* @since 4.30.0
*/
public A getAnnotation(Class annotationClass, Predicate considerOtherAnnotation) {
List annotationList = this.member.getAnnotations().asList();
return this.getContext().getAnnotationFromList(annotationClass, annotationList, considerOtherAnnotation);
}
/**
* Return the annotation of the given type on the member's container item (i.e. first type parameter if there is one), if such an annotation is
* present on either the field or its getter.
*
* @param type of annotation
* @param annotationClass type of annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present)
*/
public A getContainerItemAnnotation(Class annotationClass) {
return this.getContainerItemAnnotation(annotationClass, TypeContext.IGNORE_ANNOTATIONS_ON_ANNOTATIONS);
}
/**
* Return the annotation of the given type on the member's container item (i.e. first type parameter if there is one), if such an annotation is
* present on either the field or its getter.
*
* Additionally, also consider annotations on annotations, if the given predicate indicates another annotation as eligible for holding the target.
*
* @param type of annotation
* @param annotationClass type of annotation
* @param considerOtherAnnotation check whether some other annotation should also be checked for holding an instance of the target annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present)
*
* @since 4.30.0
*/
public abstract A getContainerItemAnnotation(Class annotationClass, Predicate considerOtherAnnotation);
/**
* Return the annotation of the given type on the member, if such an annotation is present on either the field or its getter.
*
* @param type of annotation
* @param annotationClass type of annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present)
*/
public A getAnnotationConsideringFieldAndGetter(Class annotationClass) {
return this.getAnnotationConsideringFieldAndGetter(annotationClass, TypeContext.IGNORE_ANNOTATIONS_ON_ANNOTATIONS);
}
/**
* Return the annotation of the given type on the member, if such an annotation is present on either the field or its getter.
*
* Additionally, also consider annotations on annotations, if the given predicate indicates another annotation as eligible for holding the target.
*
* @param type of annotation
* @param annotationClass type of annotation
* @param considerOtherAnnotation check whether some other annotation should also be checked for holding an instance of the target annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present)
*
* @since 4.30.0
*/
public abstract A getAnnotationConsideringFieldAndGetter(Class annotationClass,
Predicate considerOtherAnnotation);
/**
* Return the annotation of the given type on the member, if such an annotation is present on either the field or its getter and this is not a
* {@link #isFakeContainerItemScope() fake container item scope}.
*
* @param type of annotation
* @param annotationClass type of annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present or the look-up is not supported by default)
*/
public A getAnnotationConsideringFieldAndGetterIfSupported(Class annotationClass) {
return this.getAnnotationConsideringFieldAndGetterIfSupported(annotationClass, TypeContext.IGNORE_ANNOTATIONS_ON_ANNOTATIONS);
}
/**
* Return the annotation of the given type on the member, if such an annotation is present on either the field or its getter and this is not a
* {@link #isFakeContainerItemScope() fake container item scope}.
*
* Additionally, also consider annotations on annotations, if the given predicate indicates another annotation as eligible for holding the target.
*
* @param type of annotation
* @param annotationClass type of annotation
* @param considerOtherAnnotation check whether some other annotation should also be checked for holding an instance of the target annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present or the look-up is not supported by default)
*
* @since 4.30.0
*/
public A getAnnotationConsideringFieldAndGetterIfSupported(Class annotationClass,
Predicate considerOtherAnnotation) {
if (this.isFakeContainerItemScope()) {
return null;
}
return this.getAnnotationConsideringFieldAndGetter(annotationClass, considerOtherAnnotation);
}
/**
* Return the annotation of the given type on the member's container item (i.e. single type parameter if there is one), if such an annotation is
* present on either the field or its getter.
*
* @param type of annotation
* @param annotationClass type of annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present)
*/
public A getContainerItemAnnotationConsideringFieldAndGetter(Class annotationClass) {
return this.getContainerItemAnnotationConsideringFieldAndGetter(annotationClass, TypeContext.IGNORE_ANNOTATIONS_ON_ANNOTATIONS);
}
/**
* Return the annotation of the given type on the member's container item (i.e. single type parameter if there is one), if such an annotation is
* present on either the field or its getter.
*
* Additionally, also consider annotations on annotations, if the given predicate indicates another annotation as eligible for holding the target.
*
* @param type of annotation
* @param annotationClass type of annotation
* @param considerOtherAnnotation check whether some other annotation should also be checked for holding an instance of the target annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present)
*
* @since 4.30.0
*/
public abstract A getContainerItemAnnotationConsideringFieldAndGetter(Class annotationClass,
Predicate considerOtherAnnotation);
/**
* Return the annotation of the given type on the member's container item (i.e. single type parameter if there is one), if such an annotation is
* present on either the field or its getter and this particular member is either a collection or special generic type (e.g. {@link Optional}).
*
* @param type of annotation
* @param annotationClass type of annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present or this look-up is not supported by default )
*/
public A getContainerItemAnnotationConsideringFieldAndGetterIfSupported(Class annotationClass) {
return this.getContainerItemAnnotationConsideringFieldAndGetterIfSupported(annotationClass, TypeContext.IGNORE_ANNOTATIONS_ON_ANNOTATIONS);
}
/**
* Return the annotation of the given type on the member's container item (i.e. single type parameter if there is one), if such an annotation is
* present on either the field or its getter and this particular member is either a collection or special generic type (e.g. {@link Optional}).
*
* Additionally, also consider annotations on annotations, if the given predicate indicates another annotation as eligible for holding the target.
*
* @param type of annotation
* @param annotationClass type of annotation
* @param considerOtherAnnotation check whether some other annotation should also be checked for holding an instance of the target annotation
* @return annotation instance (or {@code null} if no annotation of the given type is present or this look-up is not supported by default)
*
* @since 4.30.0
*/
public A getContainerItemAnnotationConsideringFieldAndGetterIfSupported(Class annotationClass,
Predicate considerOtherAnnotation) {
if (this.isFakeContainerItemScope()) {
return this.getContainerItemAnnotationConsideringFieldAndGetter(annotationClass, considerOtherAnnotation);
}
// in addition to an explicitly marked "faked container item scope", also support a type wrapped in an Optional
if (this.getOverriddenType() == null || this.getDeclaredType().getErasedType() != Optional.class) {
return null;
}
if (this.getOverriddenType().getErasedType() == this.getDeclaredType().getTypeParameters().get(0).getErasedType()) {
return this.getContainerItemAnnotationConsideringFieldAndGetter(annotationClass, considerOtherAnnotation);
}
return null;
}
/**
* Returns the name to be used to reference this member in its parent's "properties".
*
* @return member's name in parent "properties"
*/
public String getSchemaPropertyName() {
return this.schemaPropertyName.get();
}
/**
* Returns the name to be used to reference this member in its parent's "properties".
*
* @return member's name in parent "properties"
*/
protected abstract String doGetSchemaPropertyName();
@Override
public String toString() {
return this.getSimpleTypeDescription() + " " + this.getSchemaPropertyName();
}
public static class DeclarationDetails {
private final ResolvedType schemaTargetType;
private final ResolvedTypeWithMembers declaringTypeMembers;
public DeclarationDetails(ResolvedType schemaTargetType, ResolvedTypeWithMembers declaringTypeMembers) {
this.schemaTargetType = schemaTargetType;
this.declaringTypeMembers = declaringTypeMembers;
}
/**
* Getter for the specific type for which a schema is being generated, that includes this field/method. This can differ from the wrapped
* member's declaring type, if that declaring type is an implemented interface or super type of the targeted one.
*
* @return target type for which a schema is being generated including this field/method
*/
public ResolvedType getSchemaTargetType() {
return this.schemaTargetType;
}
/**
* Getter for the collection of the member's declaring type's (other) fields and methods.
*
* @return declaring type's fields and methods
*/
public ResolvedTypeWithMembers getDeclaringTypeMembers() {
return this.declaringTypeMembers;
}
}
static class OverrideDetails {
private final ResolvedType overriddenType;
private final String overriddenName;
private final Integer fakeContainerItemIndex;
/**
* Constructor.
*
* @param overriddenType alternative type for this field or method's return value
* @param overriddenName alternative name for this field or method
* @param fakeContainerItemIndex index of the container item on the generic field/method's declared type (e.g., in case of a List, it is 0)
*/
OverrideDetails(ResolvedType overriddenType, String overriddenName, Integer fakeContainerItemIndex) {
this.overriddenType = overriddenType;
this.overriddenName = overriddenName;
this.fakeContainerItemIndex = fakeContainerItemIndex;
}
private static ResolvedType getOverriddenType(OverrideDetails details) {
return details == null ? null : details.overriddenType;
}
private static String getOverriddenName(OverrideDetails details) {
return details == null ? null : details.overriddenName;
}
private static Integer getFakeContainerItemIndex(OverrideDetails details) {
return details == null ? null : details.fakeContainerItemIndex;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy