blob: b12b5419706b681b984ae8c578c675ff5c8e1e04 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.bval.jsr.metadata;
import java.lang.annotation.ElementType;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Map;
import java.util.Objects;
import org.apache.bval.util.EmulatedAnnotatedType;
import org.apache.bval.util.Lazy;
import org.apache.bval.util.Validate;
import org.apache.bval.util.reflection.TypeUtils;
/**
* Validation class model.
*
* @param <E>
*/
public abstract class Meta<E extends AnnotatedElement> {
public static class ForClass<T> extends Meta<Class<T>> {
private final AnnotatedType annotatedType;
public ForClass(Class<T> host) {
super(host, ElementType.TYPE);
this.annotatedType = EmulatedAnnotatedType.wrap(host);
}
@Override
public final Class<T> getDeclaringClass() {
return getHost();
}
@Override
public Type getType() {
return getHost();
}
@Override
public AnnotatedType getAnnotatedType() {
return annotatedType;
}
@Override
public String getName() {
return getHost().getName();
}
@Override
public Meta<?> getParent() {
return null;
}
}
public static abstract class ForMember<M extends Member & AnnotatedElement> extends Meta<M> {
@SuppressWarnings({ "rawtypes", "unchecked" })
private final Lazy<Meta<Class<?>>> parent = new Lazy<>(() -> new Meta.ForClass(getDeclaringClass()));
protected ForMember(M host, ElementType elementType) {
super(host, elementType);
}
@Override
public Class<?> getDeclaringClass() {
return getHost().getDeclaringClass();
}
@Override
public Meta<Class<?>> getParent() {
return parent.get();
}
}
public static class ForField extends ForMember<Field> {
public ForField(Field host) {
super(host, ElementType.FIELD);
}
@Override
public Type getType() {
return getHost().getGenericType();
}
@Override
public AnnotatedType getAnnotatedType() {
return getHost().getAnnotatedType();
}
@Override
public String getName() {
return getHost().getName();
}
}
public static abstract class ForExecutable<E extends Executable> extends ForMember<E> {
protected ForExecutable(E host, ElementType elementType) {
super(host, elementType);
}
@Override
public AnnotatedType getAnnotatedType() {
return getHost().getAnnotatedReturnType();
}
}
public static class ForConstructor<T> extends ForExecutable<Constructor<? extends T>> {
public ForConstructor(Constructor<? extends T> host) {
super(host, ElementType.CONSTRUCTOR);
}
@Override
public Type getType() {
return getHost().getDeclaringClass();
}
@Override
public String getName() {
return getHost().getDeclaringClass().getSimpleName();
}
}
public static class ForMethod extends ForExecutable<Method> {
public ForMethod(Method host) {
super(host, ElementType.METHOD);
}
@Override
public Type getType() {
return getHost().getGenericReturnType();
}
@Override
public String getName() {
return getHost().getName();
}
}
public static class ForCrossParameter<E extends Executable> extends Meta<E> {
private final Meta<E> parent;
public ForCrossParameter(Meta<E> parent) {
super(parent.getHost(), parent.getElementType());
this.parent = parent;
}
@Override
public Type getType() {
return Object[].class;
}
@Override
public String getName() {
return "<cross parameter>";
}
@Override
public String describeHost() {
return String.format("%s of %s", getName(), getHost());
}
@Override
public Meta<E> getParent() {
return parent;
}
@Override
public Class<?> getDeclaringClass() {
return getHost().getDeclaringClass();
}
@Override
public AnnotatedType getAnnotatedType() {
return getHost().getAnnotatedReturnType();
}
}
public static class ForParameter extends Meta<Parameter> {
private final String name;
private final Lazy<Meta<? extends Executable>> parent = new Lazy<>(this::computeParent);
public ForParameter(Parameter host, String name) {
super(host, ElementType.PARAMETER);
this.name = Validate.notNull(name, "name");
}
@Override
public Type getType() {
return getHost().getParameterizedType();
}
@Override
public Class<?> getDeclaringClass() {
return getHost().getDeclaringExecutable().getDeclaringClass();
}
@Override
public AnnotatedType getAnnotatedType() {
return getHost().getAnnotatedType();
}
@Override
public String getName() {
return name;
}
@Override
public String describeHost() {
return String.format("%s of %s", getName(), getHost().getDeclaringExecutable());
}
@Override
public Meta<? extends Executable> getParent() {
return parent.get();
}
private Meta<? extends Executable> computeParent() {
final Executable exe = getHost().getDeclaringExecutable();
return exe instanceof Method ? new Meta.ForMethod((Method) exe)
: new Meta.ForConstructor<>((Constructor<?>) exe);
}
}
public static class ForContainerElement extends Meta<AnnotatedType> {
private final Meta<?> parent;
private final ContainerElementKey key;
public ForContainerElement(Meta<?> parent, ContainerElementKey key) {
super(key.getAnnotatedType(), ElementType.TYPE_USE);
this.parent = Validate.notNull(parent, "parent");
this.key = Validate.notNull(key, "key");
}
@Override
public Type getType() {
Type result = getHost().getType();
if (result instanceof TypeVariable<?>) {
final Type parentType = parent.getType();
if (parentType instanceof ParameterizedType) {
final Map<TypeVariable<?>, Type> typeArguments =
TypeUtils.getTypeArguments((ParameterizedType) parentType);
if (typeArguments.containsKey(result)) {
return typeArguments.get(result);
}
}
}
return result;
}
@Override
public Class<?> getDeclaringClass() {
return parent.getDeclaringClass();
}
@Override
public AnnotatedType getAnnotatedType() {
return key.getAnnotatedType();
}
public Integer getTypeArgumentIndex() {
return key.getTypeArgumentIndex();
}
@Override
public String getName() {
return key.toString();
}
@Override
public String describeHost() {
return String.format("%s of %s", key, parent);
}
@Override
public Meta<?> getParent() {
return parent;
}
}
private final E host;
private final ElementType elementType;
protected Meta(E host, ElementType elementType) {
super();
this.host = Validate.notNull(host, "host");
this.elementType = Validate.notNull(elementType, "elementType");
}
public E getHost() {
return host;
}
public ElementType getElementType() {
return elementType;
}
public abstract Type getType();
public abstract Class<?> getDeclaringClass();
public abstract AnnotatedType getAnnotatedType();
public abstract String getName();
public abstract Meta<?> getParent();
@Override
public final String toString() {
return String.format("%s.%s(%s)", Meta.class.getSimpleName(), getClass().getSimpleName(), describeHost());
}
public String describeHost() {
return host.toString();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!obj.getClass().equals(getClass())) {
return false;
}
return Objects.equals(((Meta<?>) obj).getHost(), getHost());
}
@Override
public int hashCode() {
return Objects.hash(getHost());
}
}