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

org.hibernate.metamodel.source.annotations.xml.mocker.IndexBuilder Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2011, Red Hat Inc. or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Inc..
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.metamodel.source.annotations.xml.mocker;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.logging.Logger;

import org.hibernate.AssertionFailure;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.source.annotations.xml.filter.IndexedAnnotationFilter;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.classloading.spi.ClassLoaderService;

/**
 * @author Strong Liu
 */
public class IndexBuilder {
	private static final CoreMessageLogger LOG = Logger.getMessageLogger(
			CoreMessageLogger.class,
			IndexBuilder.class.getName()
	);
	private Map> annotations;
	private Map> subclasses;
	private Map> implementors;
	private Map classes;
	private Index index;
	private Map>> classInfoAnnotationsMap;
	private Map>> indexedClassInfoAnnotationsMap;
	private ServiceRegistry serviceRegistry;

	IndexBuilder(Index index, ServiceRegistry serviceRegistry) {
		this.index = index;
		this.serviceRegistry = serviceRegistry;
		this.annotations = new HashMap>();
		this.subclasses = new HashMap>();
		this.implementors = new HashMap>();
		this.classes = new HashMap();
		this.classInfoAnnotationsMap = new HashMap>>();
		this.indexedClassInfoAnnotationsMap = new HashMap>>();
	}

	/**
	 * Build new {@link Index} with mocked annotations from orm.xml.
	 * This method should be only called once per {@org.hibernate.metamodel.source.annotations.xml.mocker.IndexBuilder IndexBuilder} instance.
	 *
	 * @param globalDefaults Global defaults from , or null.
	 *
	 * @return Index.
	 */
	Index build(EntityMappingsMocker.Default globalDefaults) {
		//merge annotations that not overrided by xml into the new Index
		for ( ClassInfo ci : index.getKnownClasses() ) {
			DotName name = ci.name();
			if ( indexedClassInfoAnnotationsMap.containsKey( name ) ) {
				//this class has been overrided by orm.xml
				continue;
			}
			if ( ci.annotations() != null && !ci.annotations().isEmpty() ) {
				Map> tmp = new HashMap>( ci.annotations() );
				DefaultConfigurationHelper.INSTANCE.applyDefaults( tmp, globalDefaults );
				mergeAnnotationMap( tmp, annotations );
				classes.put( name, ci );
				if ( ci.superName() != null ) {
					addSubClasses( ci.superName(), ci );
				}
				if ( ci.interfaces() != null && ci.interfaces().length > 0 ) {
					addImplementors( ci.interfaces(), ci );
				}
			}
		}
		return Index.create(
				annotations, subclasses, implementors, classes
		);
	}
	Map> getAnnotations(){
		return Collections.unmodifiableMap( annotations );
	}
	/**
	 * If {@code xml-mapping-metadata-complete} is defined in PersistenceUnitMetadata, we create a new empty {@link Index} here.
	 */
	void mappingMetadataComplete() {
		LOG.debug(
				"xml-mapping-metadata-complete is specified in persistence-unit-metadata, ignore JPA annotations."
		);
		index = Index.create(
				new HashMap>(),
				new HashMap>(),
				new HashMap>(),
				new HashMap()
		);
	}

	/**
	 * @param name Entity Object dot name which is being process.
	 */
	void metadataComplete(DotName name) {
		LOG.debug(
				"metadata-complete is specified in " + name + ", ignore JPA annotations."
		);
		getIndexedAnnotations( name ).clear();
	}

	public Map> getIndexedAnnotations(DotName name) {
		Map> map = indexedClassInfoAnnotationsMap.get( name );
		if ( map == null ) {
			ClassInfo ci = index.getClassByName( name );
			if ( ci == null || ci.annotations() == null ) {
				map = Collections.emptyMap();
			}
			else {
				map = new HashMap>( ci.annotations() );
				//here we ignore global annotations
				for ( DotName globalAnnotationName : DefaultConfigurationHelper.GLOBAL_ANNOTATIONS ) {
					if ( map.containsKey( globalAnnotationName ) ) {
						map.put( globalAnnotationName, Collections.emptyList() );
					}
				}
			}
			indexedClassInfoAnnotationsMap.put( name, map );
		}
		return map;
	}

	public Map> getClassInfoAnnotationsMap(DotName name) {
		return classInfoAnnotationsMap.get( name );
	}

	public ClassInfo getClassInfo(DotName name) {
		return classes.get( name );
	}

	public ClassInfo getIndexedClassInfo(DotName name) {
		return index.getClassByName( name );
	}

	void collectGlobalConfigurationFromIndex(GlobalAnnotations globalAnnotations) {
		for ( DotName annName : DefaultConfigurationHelper.GLOBAL_ANNOTATIONS ) {
			List annotationInstanceList = index.getAnnotations( annName );
			if ( MockHelper.isNotEmpty( annotationInstanceList ) ) {
				globalAnnotations.addIndexedAnnotationInstance( annotationInstanceList );
			}
		}
		globalAnnotations.filterIndexedAnnotations();
	}

	void finishGlobalConfigurationMocking(GlobalAnnotations globalAnnotations) {
		annotations.putAll( globalAnnotations.getAnnotationInstanceMap() );
	}

	void finishEntityObject(final DotName name, final EntityMappingsMocker.Default defaults) {
		Map> map = classInfoAnnotationsMap.get( name );
		if ( map == null ) {
			throw new AssertionFailure( "Calling finish entity object " + name + " before create it." );
		}
		// annotations classes overrided by xml
		if ( indexedClassInfoAnnotationsMap.containsKey( name ) ) {
			Map> tmp = getIndexedAnnotations( name );
			mergeAnnotationMap( tmp, map );
		}
		DefaultConfigurationHelper.INSTANCE.applyDefaults( map, defaults );
		
		mergeAnnotationMap( map, annotations );
	}


	void addAnnotationInstance(DotName targetClassName, AnnotationInstance annotationInstance) {
		if ( annotationInstance == null ) {
			return;
		}
		for ( IndexedAnnotationFilter indexedAnnotationFilter : IndexedAnnotationFilter.ALL_FILTERS ) {
			indexedAnnotationFilter.beforePush( this, targetClassName, annotationInstance );
		}
		Map> map = classInfoAnnotationsMap.get( targetClassName );
		if ( map == null ) {
			throw new AssertionFailure( "Can't find " + targetClassName + " in internal cache, should call createClassInfo first" );
		}

		List annotationInstanceList = map.get( annotationInstance.name() );
		if ( annotationInstanceList == null ) {
			annotationInstanceList = new ArrayList();
			map.put( annotationInstance.name(), annotationInstanceList );
		}
		annotationInstanceList.add( annotationInstance );
	}

	ServiceRegistry getServiceRegistry() {
		return serviceRegistry;
	}

	ClassInfo createClassInfo(String className) {
		if ( StringHelper.isEmpty( className ) ) {
			throw new AssertionFailure( "Class Name used to create ClassInfo is empty." );
		}
		DotName classDotName = DotName.createSimple( className );
		if ( classes.containsKey( classDotName ) ) {
			//classInfoAnnotationsMap.put( classDotName, new HashMap>(classes.get( classDotName ).annotations()) );
			return classes.get( classDotName );
		}
		Class clazz = serviceRegistry.getService( ClassLoaderService.class ).classForName( className );
		DotName superName = null;
		DotName[] interfaces = null;
		short access_flag;
		ClassInfo annClassInfo = index.getClassByName( classDotName );
		if ( annClassInfo != null ) {
			superName = annClassInfo.superName();
			interfaces = annClassInfo.interfaces();
			access_flag = annClassInfo.flags();
		}
		else {
			Class superClass = clazz.getSuperclass();
			if ( superClass != null ) {
				superName = DotName.createSimple( superClass.getName() );
			}
			Class[] classInterfaces = clazz.getInterfaces();
			if ( classInterfaces != null && classInterfaces.length > 0 ) {
				interfaces = new DotName[classInterfaces.length];
				for ( int i = 0; i < classInterfaces.length; i++ ) {
					interfaces[i] = DotName.createSimple( classInterfaces[i].getName() );
				}
			}
			access_flag = (short) ( clazz.getModifiers() | 0x20 );//(modifiers | ACC_SUPER)
		}
		Map> map = new HashMap>();
		classInfoAnnotationsMap.put( classDotName, map );
		ClassInfo classInfo = ClassInfo.create(
				classDotName, superName, access_flag, interfaces, map
		);
		classes.put( classDotName, classInfo );
		addSubClasses( superName, classInfo );
		addImplementors( interfaces, classInfo );
		return classInfo;
	}

	private void addSubClasses(DotName superClassDotName, ClassInfo classInfo) {
		if ( superClassDotName != null ) {
			List classInfoList = subclasses.get( superClassDotName );
			if ( classInfoList == null ) {
				classInfoList = new ArrayList();
				subclasses.put( superClassDotName, classInfoList );
			}
			classInfoList.add( classInfo );
		}
	}

	private void addImplementors(DotName[] dotNames, ClassInfo classInfo) {
		if ( dotNames != null && dotNames.length > 0 ) {
			for ( DotName dotName : dotNames ) {
				List classInfoList = implementors.get( dotName );
				if ( classInfoList == null ) {
					classInfoList = new ArrayList();
					implementors.put( dotName, classInfoList );
				}
				classInfoList.add( classInfo );
			}
		}
	}

	//merge source into target
	private void mergeAnnotationMap(Map> source, Map> target) {
		if ( source != null ) {
			for ( Map.Entry> el : source.entrySet() ) {
				if ( el.getValue().isEmpty() ) {
					continue;
				}
				DotName annotationName = el.getKey();
				List value = el.getValue();
				List annotationInstanceList = target.get( annotationName );
				if ( annotationInstanceList == null ) {
					annotationInstanceList = new ArrayList();
					target.put( annotationName, annotationInstanceList );
				}
				annotationInstanceList.addAll( value );
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy