All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.cfg.AttributeConverterDefinition Maven / Gradle / Ivy

There is a newer version: 6.5.0.CR2
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.cfg;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.boot.AttributeConverterInfo;
import org.hibernate.boot.model.convert.internal.InstanceBasedConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;

/**
 * Externalized representation of an AttributeConverter
 *
 * @author Steve Ebersole
 *
 * @deprecated (since 5.3) forces the converter instance to be built too early,
 * which precludes the ability to resolve them from CDI, etc.  See
 * {@link org.hibernate.boot.model.convert.spi.ConverterDescriptor} instead
 */
@Deprecated
public class AttributeConverterDefinition implements AttributeConverterInfo {
	private final AttributeConverter attributeConverter;
	private final boolean autoApply;
	private final Class entityAttributeType;
	private final Class databaseColumnType;

	/**
	 * Build an AttributeConverterDefinition from the AttributeConverter Class reference and
	 * whether or not to auto-apply it.
	 *
	 * @param attributeConverterClass The AttributeConverter Class
	 * @param autoApply Should the AttributeConverter be auto-applied?
	 *
	 * @return The constructed definition
	 */
	public static AttributeConverterDefinition from(Class attributeConverterClass, boolean autoApply) {
		return new AttributeConverterDefinition(
				instantiateAttributeConverter( attributeConverterClass ),
				autoApply
		);
	}

	private static AttributeConverter instantiateAttributeConverter(Class attributeConverterClass) {
		try {
			return attributeConverterClass.newInstance();
		}
		catch (Exception e) {
			throw new AnnotationException(
					"Unable to instantiate AttributeConverter [" + attributeConverterClass.getName() + "]",
					e
			);
		}
	}

	/**
	 * Build an AttributeConverterDefinition from the AttributeConverter Class reference.  The
	 * converter is searched for a {@link Converter} annotation	 to determine whether it should
	 * be treated as auto-apply.  If the annotation is present, {@link Converter#autoApply()} is
	 * used to make that determination.  If the annotation is not present, {@code false} is assumed.
	 *
	 * @param attributeConverterClass The converter class
	 *
	 * @return The constructed definition
	 */
	public static AttributeConverterDefinition from(Class attributeConverterClass) {
		return from( instantiateAttributeConverter( attributeConverterClass ) );
	}

	/**
	 * Build an AttributeConverterDefinition from an AttributeConverter instance.  The
	 * converter is searched for a {@link Converter} annotation	 to determine whether it should
	 * be treated as auto-apply.  If the annotation is present, {@link Converter#autoApply()} is
	 * used to make that determination.  If the annotation is not present, {@code false} is assumed.
	 *
	 * @param attributeConverter The AttributeConverter instance
	 *
	 * @return The constructed definition
	 */
	public static AttributeConverterDefinition from(AttributeConverter attributeConverter) {
		boolean autoApply = false;
		Converter converterAnnotation = attributeConverter.getClass().getAnnotation( Converter.class );
		if ( converterAnnotation != null ) {
			autoApply = converterAnnotation.autoApply();
		}

		return new AttributeConverterDefinition( attributeConverter, autoApply );
	}

	/**
	 * Build an AttributeConverterDefinition from the AttributeConverter instance and
	 * whether or not to auto-apply it.
	 *
	 * @param attributeConverter The AttributeConverter instance
	 * @param autoApply Should the AttributeConverter be auto-applied?
	 *
	 * @return The constructed definition
	 */
	public static AttributeConverterDefinition from(AttributeConverter attributeConverter, boolean autoApply) {
		return new AttributeConverterDefinition( attributeConverter, autoApply );
	}

	public AttributeConverterDefinition(AttributeConverter attributeConverter, boolean autoApply) {
		this.attributeConverter = attributeConverter;
		this.autoApply = autoApply;

		final Class attributeConverterClass = attributeConverter.getClass();
		final ParameterizedType attributeConverterSignature = extractAttributeConverterParameterizedType( attributeConverterClass );
		if ( attributeConverterSignature == null ) {
			throw new AssertionFailure(
					"Could not extract ParameterizedType representation of AttributeConverter definition " +
							"from AttributeConverter implementation class [" + attributeConverterClass.getName() + "]"
			);
		}

		if ( attributeConverterSignature.getActualTypeArguments().length < 2 ) {
			throw new AnnotationException(
					"AttributeConverter [" + attributeConverterClass.getName()
							+ "] did not retain parameterized type information"
			);
		}

		if ( attributeConverterSignature.getActualTypeArguments().length > 2 ) {
			throw new AnnotationException(
					"AttributeConverter [" + attributeConverterClass.getName()
							+ "] specified more than 2 parameterized types"
			);
		}
		entityAttributeType = extractClass( attributeConverterSignature.getActualTypeArguments()[0] );
		if ( entityAttributeType == null ) {
			throw new AnnotationException(
					"Could not determine 'entity attribute' type from given AttributeConverter [" +
							attributeConverterClass.getName() + "]"
			);
		}

		databaseColumnType = extractClass(attributeConverterSignature.getActualTypeArguments()[1]);
		if ( databaseColumnType == null ) {
			throw new AnnotationException(
					"Could not determine 'database column' type from given AttributeConverter [" +
							attributeConverterClass.getName() + "]"
			);
		}
	}

	private ParameterizedType extractAttributeConverterParameterizedType(Type base) {
		if ( base != null ) {
			Class clazz = extractClass( base );
			List types = new ArrayList<>();
			types.add( clazz.getGenericSuperclass() );
			types.addAll( Arrays.asList( clazz.getGenericInterfaces() ) );
			for ( Type type : types ) {
				type = resolveType( type, base );
				if ( ParameterizedType.class.isInstance( type ) ) {
					final ParameterizedType parameterizedType = (ParameterizedType) type;
					if ( AttributeConverter.class.equals( parameterizedType.getRawType() ) ) {
						return parameterizedType;
					}
				}
				ParameterizedType parameterizedType = extractAttributeConverterParameterizedType( type );
				if ( parameterizedType != null ) {
					return parameterizedType;
				}
			}
		}
		return null;
	}

	private static Type resolveType(Type target, Type context) {
		if ( target instanceof ParameterizedType ) {
			return resolveParameterizedType( (ParameterizedType) target, context );
		}
		else if ( target instanceof TypeVariable ) {
			return resolveTypeVariable( (TypeVariable) target, (ParameterizedType) context );
		}
		return target;
	}

	private static ParameterizedType resolveParameterizedType(final ParameterizedType parameterizedType, Type context) {
		Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

		final Type[] resolvedTypeArguments = new Type[actualTypeArguments.length];
		for ( int idx = 0; idx < actualTypeArguments.length; idx++ ) {
			resolvedTypeArguments[idx] = resolveType( actualTypeArguments[idx], context );
		}
		return new ParameterizedType() {

			@Override
			public Type[] getActualTypeArguments() {
				return resolvedTypeArguments;
			}

			@Override
			public Type getRawType() {
				return parameterizedType.getRawType();
			}

			@Override
			public Type getOwnerType() {
				return parameterizedType.getOwnerType();
			}

		};
	}

	private static Type resolveTypeVariable(TypeVariable typeVariable, ParameterizedType context) {
		Class clazz = extractClass( context.getRawType() );
		TypeVariable[] typeParameters = clazz.getTypeParameters();
		for ( int idx = 0; idx < typeParameters.length; idx++ ) {
			if ( typeVariable.getName().equals( typeParameters[idx].getName() ) ) {
				return resolveType( context.getActualTypeArguments()[idx], context );
			}
		}
		return typeVariable;
	}

	public AttributeConverter getAttributeConverter() {
		return attributeConverter;
	}

	public boolean isAutoApply() {
		return autoApply;
	}

	public Class getEntityAttributeType() {
		return entityAttributeType;
	}

	public Class getDatabaseColumnType() {
		return databaseColumnType;
	}

	private static Class extractClass(Type type) {
		if ( type instanceof Class ) {
			return (Class) type;
		}
		else if ( type instanceof ParameterizedType ) {
			return extractClass( ( (ParameterizedType) type ).getRawType() );
		}
		return null;
	}

	@Override
	public Class getConverterClass() {
		return attributeConverter.getClass();
	}

	@Override
	public String toString() {
		return String.format(
				"%s[converterClass=%s, domainType=%s, jdbcType=%s]",
				this.getClass().getName(),
				attributeConverter.getClass().getName(),
				entityAttributeType.getName(),
				databaseColumnType.getName()
		);
	}

	@Override
	public ConverterDescriptor toConverterDescriptor(MetadataBuildingContext context) {
		return new InstanceBasedConverterDescriptor(
				getAttributeConverter(),
				isAutoApply(),
				context.getBootstrapContext().getClassmateContext()
		);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy