[hibernate-commits] Hibernate SVN: r20692 - in search/trunk/hibernate-search/src/main/java/org/hibernate/search: util and 1 other directory.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Fri Sep 24 09:53:56 EDT 2010
Author: hardy.ferentschik
Date: 2010-09-24 09:53:55 -0400 (Fri, 24 Sep 2010)
New Revision: 20692
Added:
search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/AbstractDocumentBuilder.java
Modified:
search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java
search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/DocumentBuilderIndexedEntity.java
search/trunk/hibernate-search/src/main/java/org/hibernate/search/util/HibernateHelper.java
Log:
HSEARCH-371 - added abstract base class
Copied: search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/AbstractDocumentBuilder.java (from rev 20691, search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java)
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/AbstractDocumentBuilder.java (rev 0)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/AbstractDocumentBuilder.java 2010-09-24 13:53:55 UTC (rev 20692)
@@ -0,0 +1,810 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates 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.search.engine;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.search.Similarity;
+import org.slf4j.Logger;
+
+import org.hibernate.annotations.common.AssertionFailure;
+import org.hibernate.annotations.common.reflection.ReflectionManager;
+import org.hibernate.annotations.common.reflection.XAnnotatedElement;
+import org.hibernate.annotations.common.reflection.XClass;
+import org.hibernate.annotations.common.reflection.XMember;
+import org.hibernate.annotations.common.reflection.XProperty;
+import org.hibernate.annotations.common.util.StringHelper;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.analyzer.Discriminator;
+import org.hibernate.search.annotations.AnalyzerDef;
+import org.hibernate.search.annotations.AnalyzerDefs;
+import org.hibernate.search.annotations.AnalyzerDiscriminator;
+import org.hibernate.search.annotations.Boost;
+import org.hibernate.search.annotations.ClassBridge;
+import org.hibernate.search.annotations.ClassBridges;
+import org.hibernate.search.annotations.ContainedIn;
+import org.hibernate.search.annotations.DynamicBoost;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.IndexedEmbedded;
+import org.hibernate.search.annotations.Store;
+import org.hibernate.search.annotations.TermVector;
+import org.hibernate.search.backend.LuceneWork;
+import org.hibernate.search.backend.WorkType;
+import org.hibernate.search.bridge.BridgeFactory;
+import org.hibernate.search.bridge.FieldBridge;
+import org.hibernate.search.bridge.LuceneOptions;
+import org.hibernate.search.impl.ConfigContext;
+import org.hibernate.search.util.ClassLoaderHelper;
+import org.hibernate.search.util.HibernateHelper;
+import org.hibernate.search.util.LoggerFactory;
+import org.hibernate.search.util.PassThroughAnalyzer;
+import org.hibernate.search.util.ReflectionHelper;
+import org.hibernate.search.util.ScopedAnalyzer;
+
+/**
+ * Abstract base class for the document builders.
+ *
+ * @author Hardy Ferentschik
+ */
+public abstract class AbstractDocumentBuilder<T> implements DocumentBuilder {
+ private static final Logger log = LoggerFactory.make();
+
+ protected final PropertiesMetadata metadata = new PropertiesMetadata();
+ protected final XClass beanXClass;
+ protected final Class<?> beanClass;
+ protected Set<Class<?>> mappedSubclasses = new HashSet<Class<?>>();
+ protected ReflectionManager reflectionManager; //available only during initialization and post-initialization
+ protected int level = 0;
+ protected int maxLevel = Integer.MAX_VALUE;
+ protected final ScopedAnalyzer analyzer = new ScopedAnalyzer();
+ protected Similarity similarity; //there is only 1 similarity per class hierarchy, and only 1 per index
+ protected boolean isRoot;
+ protected EntityState entityState;
+ private Analyzer passThroughAnalyzer = new PassThroughAnalyzer();
+
+ /**
+ * Constructor used on contained entities not annotated with <code>@Indexed</code> themselves.
+ *
+ * @param xClass The class for which to build a <code>DocumentBuilderContainedEntity</code>.
+ * @param context Handle to default configuration settings.
+ * @param reflectionManager Reflection manager to use for processing the annotations.
+ */
+ public AbstractDocumentBuilder(XClass xClass, ConfigContext context, ReflectionManager reflectionManager) {
+
+ if ( xClass == null ) {
+ throw new AssertionFailure( "Unable to build a DocumentBuilderContainedEntity with a null class" );
+ }
+
+ this.entityState = EntityState.CONTAINED_IN_ONLY;
+ this.beanXClass = xClass;
+ this.reflectionManager = reflectionManager;
+ this.beanClass = reflectionManager.toClass( xClass );
+ init( xClass, context );
+
+ if ( metadata.containedInGetters.size() == 0 ) {
+ this.entityState = EntityState.NON_INDEXABLE;
+ }
+ }
+
+ abstract public void addWorkToQueue(Class<T> entityClass, T entity, Serializable id, WorkType workType, List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor);
+
+ abstract protected void subClassSpecificCheck(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, ConfigContext context);
+
+ abstract protected void initSubClass(XClass clazz, ConfigContext context);
+
+ public boolean isRoot() {
+ return isRoot;
+ }
+
+ public Similarity getSimilarity() {
+ return similarity;
+ }
+
+ public Analyzer getAnalyzer() {
+ return analyzer;
+ }
+
+ public EntityState getEntityState() {
+ return entityState;
+ }
+
+ public Set<Class<?>> getMappedSubclasses() {
+ return mappedSubclasses;
+ }
+
+ public void postInitialize(Set<Class<?>> indexedClasses) {
+ //we initialize only once because we no longer have a reference to the reflectionManager
+ //in theory
+ Class<?> plainClass = beanClass;
+ if ( entityState == EntityState.NON_INDEXABLE ) {
+ throw new AssertionFailure( "A non indexed entity is post processed" );
+ }
+ Set<Class<?>> tempMappedSubclasses = new HashSet<Class<?>>();
+ //together with the caller this creates a o(2), but I think it's still faster than create the up hierarchy for each class
+ for ( Class<?> currentClass : indexedClasses ) {
+ if ( plainClass != currentClass && plainClass.isAssignableFrom( currentClass ) ) {
+ tempMappedSubclasses.add( currentClass );
+ }
+ }
+ this.mappedSubclasses = Collections.unmodifiableSet( tempMappedSubclasses );
+ Class<?> superClass = plainClass.getSuperclass();
+ this.isRoot = true;
+ while ( superClass != null ) {
+ if ( indexedClasses.contains( superClass ) ) {
+ this.isRoot = false;
+ break;
+ }
+ superClass = superClass.getSuperclass();
+ }
+ this.reflectionManager = null;
+ }
+
+ protected Analyzer getAnalyzer(XAnnotatedElement annotatedElement, ConfigContext context) {
+ org.hibernate.search.annotations.Analyzer analyzerAnn =
+ annotatedElement.getAnnotation( org.hibernate.search.annotations.Analyzer.class );
+ return getAnalyzer( analyzerAnn, context );
+ }
+
+ protected void addToScopedAnalyzer(String fieldName, Analyzer analyzer, Index index) {
+ if ( index == Index.TOKENIZED ) {
+ if ( analyzer != null ) {
+ this.analyzer.addScopedAnalyzer( fieldName, analyzer );
+ }
+ }
+ else {
+ //no analyzer is used, add a fake one for queries
+ this.analyzer.addScopedAnalyzer( fieldName, passThroughAnalyzer );
+ }
+ }
+
+ protected Float getBoost(XProperty member, org.hibernate.search.annotations.Field fieldAnn) {
+ float computedBoost = 1.0f;
+ Boost boostAnn = member.getAnnotation( Boost.class );
+ if ( boostAnn != null ) {
+ computedBoost = boostAnn.value();
+ }
+ if ( fieldAnn != null ) {
+ computedBoost *= fieldAnn.boost().value();
+ }
+ return computedBoost;
+ }
+
+ protected BoostStrategy getDynamicBoost(XProperty member) {
+ DynamicBoost boostAnnotation = member.getAnnotation( DynamicBoost.class );
+ if ( boostAnnotation == null ) {
+ return new DefaultBoostStrategy();
+ }
+
+ Class<? extends BoostStrategy> boostStrategyClass = boostAnnotation.impl();
+ BoostStrategy strategy;
+ try {
+ strategy = boostStrategyClass.newInstance();
+ }
+ catch ( Exception e ) {
+ throw new SearchException(
+ "Unable to instantiate boost strategy implementation: " + boostStrategyClass.getName()
+ );
+ }
+ return strategy;
+ }
+
+ protected Field.TermVector getTermVector(TermVector vector) {
+ switch ( vector ) {
+ case NO:
+ return Field.TermVector.NO;
+ case YES:
+ return Field.TermVector.YES;
+ case WITH_OFFSETS:
+ return Field.TermVector.WITH_OFFSETS;
+ case WITH_POSITIONS:
+ return Field.TermVector.WITH_POSITIONS;
+ case WITH_POSITION_OFFSETS:
+ return Field.TermVector.WITH_POSITIONS_OFFSETS;
+ default:
+ throw new AssertionFailure( "Unexpected TermVector: " + vector );
+ }
+ }
+
+ protected Field.Index getIndex(Index index) {
+ switch ( index ) {
+ case NO:
+ return Field.Index.NO;
+ case NO_NORMS:
+ return Field.Index.NOT_ANALYZED_NO_NORMS;
+ case TOKENIZED:
+ return Field.Index.ANALYZED;
+ case UN_TOKENIZED:
+ return Field.Index.NOT_ANALYZED;
+ default:
+ throw new AssertionFailure( "Unexpected Index: " + index );
+ }
+ }
+
+ /**
+ * If we have a work instance we have to check whether the instance to be indexed is contained in any other indexed entities.
+ *
+ * @param instance The instance to be indexed
+ * @param queue the current work queue
+ * @param metadata metadata
+ * @param searchFactoryImplementor the current session
+ */
+ protected <T> void processContainedInInstances(Object instance, List<LuceneWork> queue, PropertiesMetadata metadata, SearchFactoryImplementor searchFactoryImplementor) {
+ for ( int i = 0; i < metadata.containedInGetters.size(); i++ ) {
+ XMember member = metadata.containedInGetters.get( i );
+ Object value = ReflectionHelper.getMemberValue( instance, member );
+
+ if ( value == null ) {
+ continue;
+ }
+
+ if ( member.isArray() ) {
+ @SuppressWarnings("unchecked")
+ T[] array = ( T[] ) value;
+ for ( T arrayValue : array ) {
+ processSingleContainedInInstance( queue, searchFactoryImplementor, arrayValue );
+ }
+ }
+ else if ( member.isCollection() ) {
+ Collection<T> collection = null;
+ try {
+ collection = getActualCollection( member, value );
+ collection.size(); //load it
+ }
+ catch ( Exception e ) {
+ if ( e.getClass().getName().contains( "org.hibernate.LazyInitializationException" ) ) {
+ /* A deleted entity not having its collection initialized
+ * leads to a LIE because the colleciton is no longer attached to the session
+ *
+ * But that's ok as the collection update event has been processed before
+ * or the fk would have been cleared and thus triggering the cleaning
+ */
+ collection = null;
+ }
+ }
+ if ( collection != null ) {
+ for ( T collectionValue : collection ) {
+ processSingleContainedInInstance( queue, searchFactoryImplementor, collectionValue );
+ }
+ }
+ }
+ else {
+ processSingleContainedInInstance( queue, searchFactoryImplementor, value );
+ }
+ }
+ }
+
+ private void init(XClass clazz, ConfigContext context) {
+ metadata.boost = getBoost( clazz );
+ metadata.classBoostStrategy = getDynamicBoost( clazz );
+ metadata.analyzer = context.getDefaultAnalyzer();
+
+ Set<XClass> processedClasses = new HashSet<XClass>();
+ processedClasses.add( clazz );
+ initializeClass( clazz, metadata, true, "", processedClasses, context );
+
+ this.analyzer.setGlobalAnalyzer( metadata.analyzer );
+
+ // set the default similarity in case that after processing all classes there is still no similarity set
+ if ( this.similarity == null ) {
+ this.similarity = context.getDefaultSimilarity();
+ }
+
+ initSubClass( clazz, context );
+ }
+
+ private void initializeClass(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix,
+ Set<XClass> processedClasses, ConfigContext context) {
+ List<XClass> hierarchy = new ArrayList<XClass>();
+ for ( XClass currentClass = clazz; currentClass != null; currentClass = currentClass.getSuperclass() ) {
+ hierarchy.add( currentClass );
+ }
+
+ /*
+ * Iterate the class hierarchy top down. This allows to override the default analyzer for the properties if the class holds one
+ */
+ for ( int index = hierarchy.size() - 1; index >= 0; index-- ) {
+ XClass currentClass = hierarchy.get( index );
+
+ initializeClassLevelAnnotations( currentClass, propertiesMetadata, isRoot, prefix, context );
+
+ // rejecting non properties (ie regular methods) because the object is loaded from Hibernate,
+ // so indexing a non property does not make sense
+ List<XProperty> methods = currentClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
+ for ( XProperty method : methods ) {
+ initializeMemberLevelAnnotations(
+ method, propertiesMetadata, isRoot, prefix, processedClasses, context
+ );
+ }
+
+ List<XProperty> fields = currentClass.getDeclaredProperties( XClass.ACCESS_FIELD );
+ for ( XProperty field : fields ) {
+ initializeMemberLevelAnnotations(
+ field, propertiesMetadata, isRoot, prefix, processedClasses, context
+ );
+ }
+ }
+ }
+
+ /**
+ * Check and initialize class level annotations.
+ *
+ * @param clazz The class to process.
+ * @param propertiesMetadata The meta data holder.
+ * @param isRoot Flag indicating if the specified class is a root entity, meaning the start of a chain of indexed
+ * entities.
+ * @param prefix The current prefix used for the <code>Document</code> field names.
+ * @param context Handle to default configuration settings.
+ */
+ private void initializeClassLevelAnnotations(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, ConfigContext context) {
+
+ // check for a class level specified analyzer
+ Analyzer analyzer = getAnalyzer( clazz, context );
+ if ( analyzer != null ) {
+ propertiesMetadata.analyzer = analyzer;
+ }
+
+ // check for AnalyzerDefs annotations
+ checkForAnalyzerDefs( clazz, context );
+
+ // Check for any ClassBridges annotation.
+ ClassBridges classBridgesAnn = clazz.getAnnotation( ClassBridges.class );
+ if ( classBridgesAnn != null ) {
+ ClassBridge[] classBridges = classBridgesAnn.value();
+ for ( ClassBridge cb : classBridges ) {
+ bindClassBridgeAnnotation( prefix, propertiesMetadata, cb, context );
+ }
+ }
+
+ // Check for any ClassBridge style of annotations.
+ ClassBridge classBridgeAnn = clazz.getAnnotation( ClassBridge.class );
+ if ( classBridgeAnn != null ) {
+ bindClassBridgeAnnotation( prefix, propertiesMetadata, classBridgeAnn, context );
+ }
+
+ checkForAnalyzerDiscriminator( clazz, propertiesMetadata );
+
+ // Get similarity
+ if ( isRoot ) {
+ checkForSimilarity( clazz );
+ }
+ }
+
+ private void initializeMemberLevelAnnotations(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot,
+ String prefix, Set<XClass> processedClasses, ConfigContext context) {
+ checkForField( member, propertiesMetadata, prefix, context );
+ checkForFields( member, propertiesMetadata, prefix, context );
+ checkForAnalyzerDefs( member, context );
+ checkForAnalyzerDiscriminator( member, propertiesMetadata );
+ checkForIndexedEmbedded( member, propertiesMetadata, prefix, processedClasses, context );
+ checkForContainedIn( member, propertiesMetadata );
+ subClassSpecificCheck( member, propertiesMetadata, isRoot, prefix, context );
+ }
+
+ private Analyzer getAnalyzer(org.hibernate.search.annotations.Analyzer analyzerAnn, ConfigContext context) {
+ Class<?> analyzerClass = analyzerAnn == null ? void.class : analyzerAnn.impl();
+ if ( analyzerClass == void.class ) {
+ String definition = analyzerAnn == null ? "" : analyzerAnn.definition();
+ if ( StringHelper.isEmpty( definition ) ) {
+ return null;
+ }
+ else {
+ return context.buildLazyAnalyzer( definition );
+ }
+ }
+ else {
+ try {
+ return ClassLoaderHelper.analyzerInstanceFromClass( analyzerClass, context.getLuceneMatchVersion() );
+ }
+ catch ( ClassCastException e ) {
+ throw new SearchException(
+ "Lucene analyzer does not implement " + Analyzer.class.getName() + ": " + analyzerClass.getName(),
+ e
+ );
+ }
+ catch ( Exception e ) {
+ throw new SearchException(
+ "Failed to instantiate lucene analyzer with type " + analyzerClass.getName(), e
+ );
+ }
+ }
+ }
+
+ private void checkForAnalyzerDefs(XAnnotatedElement annotatedElement, ConfigContext context) {
+ AnalyzerDefs defs = annotatedElement.getAnnotation( AnalyzerDefs.class );
+ if ( defs != null ) {
+ for ( AnalyzerDef def : defs.value() ) {
+ context.addAnalyzerDef( def );
+ }
+ }
+ AnalyzerDef def = annotatedElement.getAnnotation( AnalyzerDef.class );
+ context.addAnalyzerDef( def );
+ }
+
+ private void checkForAnalyzerDiscriminator(XAnnotatedElement annotatedElement, PropertiesMetadata propertiesMetadata) {
+ AnalyzerDiscriminator discriminatorAnn = annotatedElement.getAnnotation( AnalyzerDiscriminator.class );
+ if ( discriminatorAnn != null ) {
+ if ( propertiesMetadata.discriminator != null ) {
+ throw new SearchException(
+ "Multiple AnalyzerDiscriminator defined in the same class hierarchy: " + beanXClass.getName()
+ );
+ }
+
+ Class<? extends Discriminator> discriminatorClass = discriminatorAnn.impl();
+ try {
+ propertiesMetadata.discriminator = discriminatorClass.newInstance();
+ }
+ catch ( Exception e ) {
+ throw new SearchException(
+ "Unable to instantiate analyzer discriminator implementation: " + discriminatorClass.getName()
+ );
+ }
+
+ if ( annotatedElement instanceof XMember ) {
+ propertiesMetadata.discriminatorGetter = ( XMember ) annotatedElement;
+ }
+ }
+ }
+
+ private void checkForFields(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, ConfigContext context) {
+ org.hibernate.search.annotations.Fields fieldsAnn =
+ member.getAnnotation( org.hibernate.search.annotations.Fields.class );
+ if ( fieldsAnn != null ) {
+ for ( org.hibernate.search.annotations.Field fieldAnn : fieldsAnn.value() ) {
+ bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
+ }
+ }
+ }
+
+ private void checkForSimilarity(XClass currClass) {
+ org.hibernate.search.annotations.Similarity similarityAnn = currClass.getAnnotation( org.hibernate.search.annotations.Similarity.class );
+ if ( similarityAnn != null ) {
+ if ( similarity != null ) {
+ throw new SearchException(
+ "Multiple Similarities defined in the same class hierarchy: " + beanXClass.getName()
+ );
+ }
+ Class<?> similarityClass = similarityAnn.impl();
+ try {
+ similarity = ( Similarity ) similarityClass.newInstance();
+ }
+ catch ( Exception e ) {
+ log.error(
+ "Exception attempting to instantiate Similarity '{}' set for {}",
+ similarityClass.getName(), beanXClass.getName()
+ );
+ }
+ }
+ }
+
+ private void checkForField(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, ConfigContext context) {
+ org.hibernate.search.annotations.Field fieldAnn =
+ member.getAnnotation( org.hibernate.search.annotations.Field.class );
+ if ( fieldAnn != null ) {
+ bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
+ }
+ }
+
+ private void checkForContainedIn(XProperty member, PropertiesMetadata propertiesMetadata) {
+ ContainedIn containedAnn = member.getAnnotation( ContainedIn.class );
+ if ( containedAnn != null ) {
+ ReflectionHelper.setAccessible( member );
+ propertiesMetadata.containedInGetters.add( member );
+ }
+ }
+
+ private void checkForIndexedEmbedded(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, Set<XClass> processedClasses, ConfigContext context) {
+ IndexedEmbedded embeddedAnn = member.getAnnotation( IndexedEmbedded.class );
+ if ( embeddedAnn != null ) {
+ int oldMaxLevel = maxLevel;
+ int potentialLevel = embeddedAnn.depth() + level;
+ if ( potentialLevel < 0 ) {
+ potentialLevel = Integer.MAX_VALUE;
+ }
+ maxLevel = potentialLevel > maxLevel ? maxLevel : potentialLevel;
+ level++;
+
+ XClass elementClass;
+ if ( void.class == embeddedAnn.targetElement() ) {
+ elementClass = member.getElementClass();
+ }
+ else {
+ elementClass = reflectionManager.toXClass( embeddedAnn.targetElement() );
+ }
+ if ( maxLevel == Integer.MAX_VALUE //infinite
+ && processedClasses.contains( elementClass ) ) {
+ throw new SearchException(
+ "Circular reference. Duplicate use of "
+ + elementClass.getName()
+ + " in root entity " + beanXClass.getName()
+ + "#" + buildEmbeddedPrefix( prefix, embeddedAnn, member )
+ );
+ }
+ if ( level <= maxLevel ) {
+ processedClasses.add( elementClass ); //push
+
+ ReflectionHelper.setAccessible( member );
+ propertiesMetadata.embeddedGetters.add( member );
+ PropertiesMetadata metadata = new PropertiesMetadata();
+ propertiesMetadata.embeddedPropertiesMetadata.add( metadata );
+ metadata.boost = getBoost( member, null );
+ //property > entity analyzer
+ Analyzer analyzer = getAnalyzer( member, context );
+ metadata.analyzer = analyzer != null ? analyzer : propertiesMetadata.analyzer;
+ String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
+ initializeClass( elementClass, metadata, false, localPrefix, processedClasses, context );
+ /**
+ * We will only index the "expected" type but that's OK, HQL cannot do down-casting either
+ */
+ if ( member.isArray() ) {
+ propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.ARRAY );
+ }
+ else if ( member.isCollection() ) {
+ if ( Map.class.equals( member.getCollectionClass() ) ) {
+ //hum subclasses etc etc??
+ propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.MAP );
+ }
+ else {
+ propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.COLLECTION );
+ }
+ }
+ else {
+ propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.OBJECT );
+ }
+
+ processedClasses.remove( elementClass ); //pop
+ }
+ else if ( log.isTraceEnabled() ) {
+ String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
+ log.trace( "depth reached, ignoring {}", localPrefix );
+ }
+
+ level--;
+ maxLevel = oldMaxLevel; //set back the the old max level
+ }
+ }
+
+ private void bindClassBridgeAnnotation(String prefix, PropertiesMetadata propertiesMetadata, ClassBridge ann, ConfigContext context) {
+ String fieldName = prefix + ann.name();
+ propertiesMetadata.classNames.add( fieldName );
+ propertiesMetadata.classStores.add( ann.store() );
+ propertiesMetadata.classIndexes.add( getIndex( ann.index() ) );
+ propertiesMetadata.classTermVectors.add( getTermVector( ann.termVector() ) );
+ propertiesMetadata.classBridges.add( BridgeFactory.extractType( ann ) );
+ propertiesMetadata.classBoosts.add( ann.boost().value() );
+
+ Analyzer analyzer = getAnalyzer( ann.analyzer(), context );
+ if ( analyzer == null ) {
+ analyzer = propertiesMetadata.analyzer;
+ }
+ if ( analyzer == null ) {
+ throw new AssertionFailure( "Analyzer should not be undefined" );
+ }
+ addToScopedAnalyzer( fieldName, analyzer, ann.index() );
+ }
+
+ private void bindFieldAnnotation(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, org.hibernate.search.annotations.Field fieldAnn, ConfigContext context) {
+ ReflectionHelper.setAccessible( member );
+ propertiesMetadata.fieldGetters.add( member );
+ String fieldName = prefix + ReflectionHelper.getAttributeName( member, fieldAnn.name() );
+ propertiesMetadata.fieldNames.add( fieldName );
+ propertiesMetadata.fieldStore.add( fieldAnn.store() );
+ propertiesMetadata.fieldIndex.add( getIndex( fieldAnn.index() ) );
+ propertiesMetadata.fieldBoosts.add( getBoost( member, fieldAnn ) );
+ propertiesMetadata.dynamicFieldBoosts.add( getDynamicBoost( member ) );
+ propertiesMetadata.fieldTermVectors.add( getTermVector( fieldAnn.termVector() ) );
+ propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( fieldAnn, member, reflectionManager ) );
+
+ // Field > property > entity analyzer
+ Analyzer analyzer = getAnalyzer( fieldAnn.analyzer(), context );
+ if ( analyzer == null ) {
+ analyzer = getAnalyzer( member, context );
+ }
+ addToScopedAnalyzer( fieldName, analyzer, fieldAnn.index() );
+ }
+
+ private String buildEmbeddedPrefix(String prefix, IndexedEmbedded embeddedAnn, XProperty member) {
+ String localPrefix = prefix;
+ if ( ".".equals( embeddedAnn.prefix() ) ) {
+ //default to property name
+ localPrefix += member.getName() + '.';
+ }
+ else {
+ localPrefix += embeddedAnn.prefix();
+ }
+ return localPrefix;
+ }
+
+ private float getBoost(XClass element) {
+ float boost = 1.0f;
+ if ( element == null ) {
+ return boost;
+ }
+ Boost boostAnnotation = element.getAnnotation( Boost.class );
+ if ( boostAnnotation != null ) {
+ boost = boostAnnotation.value();
+ }
+ return boost;
+ }
+
+ private BoostStrategy getDynamicBoost(XClass element) {
+ if ( element == null ) {
+ return null;
+ }
+ DynamicBoost boostAnnotation = element.getAnnotation( DynamicBoost.class );
+ if ( boostAnnotation == null ) {
+ return new DefaultBoostStrategy();
+ }
+
+ Class<? extends BoostStrategy> boostStrategyClass = boostAnnotation.impl();
+ BoostStrategy strategy;
+ try {
+ strategy = boostStrategyClass.newInstance();
+ }
+ catch ( Exception e ) {
+ throw new SearchException(
+ "Unable to instantiate boost strategy implementation: " + boostStrategyClass.getName()
+ );
+ }
+ return strategy;
+ }
+
+ /**
+ * A {@code XMember } instance treats a map as a collection as well in which case the map values are returned as
+ * collection.
+ *
+ * @param member The member instance
+ * @param value The value
+ *
+ * @return The {@code value} casted to collection or in case of {@code value} being a map the map values as collection.
+ */
+ private <T> Collection<T> getActualCollection(XMember member, Object value) {
+ Collection<T> collection;
+ if ( Map.class.equals( member.getCollectionClass() ) ) {
+ //hum
+ @SuppressWarnings("unchecked")
+ Collection<T> tmpCollection = ( ( Map<?, T> ) value ).values();
+ collection = tmpCollection;
+ }
+ else {
+ @SuppressWarnings("unchecked")
+ Collection<T> tmpCollection = ( Collection<T> ) value;
+ collection = tmpCollection;
+ }
+ return collection;
+ }
+
+ private <T> void processSingleContainedInInstance(List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor, T value) {
+ Class<T> valueClass = HibernateHelper.getClass( value );
+ DocumentBuilderIndexedEntity<T> builderIndexedEntity =
+ searchFactoryImplementor.getDocumentBuilderIndexedEntity( valueClass );
+
+ // it could be we have a nested @IndexedEmbedded chain in which case we have to find the top level @Indexed entities
+ if ( builderIndexedEntity == null ) {
+ DocumentBuilderContainedEntity<T> builderContainedEntity =
+ searchFactoryImplementor.getDocumentBuilderContainedEntity( valueClass );
+ if ( builderContainedEntity != null ) {
+ processContainedInInstances( value, queue, builderContainedEntity.metadata, searchFactoryImplementor );
+ }
+ }
+ else {
+ addWorkForEmbeddedValue( value, queue, valueClass, builderIndexedEntity, searchFactoryImplementor );
+ }
+ }
+
+ /**
+ * Create a {@code LuceneWork} instance of the entity which needs updating due to the embedded instance change.
+ *
+ * @param value The value to index
+ * @param queue The current (Lucene) work queue
+ * @param valueClass The class of the value
+ * @param builderIndexedEntity the document builder for the entity which needs updating due to a update event of the embedded instance
+ * @param searchFactoryImplementor the search factory.
+ */
+ private <T> void addWorkForEmbeddedValue(T value, List<LuceneWork> queue, Class<T> valueClass,
+ DocumentBuilderIndexedEntity<T> builderIndexedEntity, SearchFactoryImplementor searchFactoryImplementor) {
+ Serializable id = ( Serializable ) ReflectionHelper.getMemberValue( value, builderIndexedEntity.idGetter );
+ if ( id != null ) {
+ builderIndexedEntity.addWorkToQueue(
+ valueClass, value, id, WorkType.UPDATE, queue, searchFactoryImplementor
+ );
+ }
+ else {
+ //this is an indexed entity that is not yet persisted but should be reached by cascade
+ // and thus raise an Hibernate Core event leading to its indexing by Hibernate Search
+ // => no need to do anything here
+ }
+ }
+
+ /**
+ * Wrapper class containing all the meta data extracted out of a single entity.
+ * All field/property related properties are kept in lists. Retrieving all metadata for a given
+ * property/field means accessing all the lists with the same index.
+ */
+ protected static class PropertiesMetadata {
+ public float boost;
+ public Analyzer analyzer;
+ public Discriminator discriminator;
+ public XMember discriminatorGetter;
+ public BoostStrategy classBoostStrategy;
+
+ public final List<String> fieldNames = new ArrayList<String>();
+ public final List<XMember> fieldGetters = new ArrayList<XMember>();
+ public final List<FieldBridge> fieldBridges = new ArrayList<FieldBridge>();
+ public final List<Store> fieldStore = new ArrayList<Store>();
+ public final List<Field.Index> fieldIndex = new ArrayList<Field.Index>();
+ public final List<Float> fieldBoosts = new ArrayList<Float>();
+ public final List<BoostStrategy> dynamicFieldBoosts = new ArrayList<BoostStrategy>();
+
+ public final List<Field.TermVector> fieldTermVectors = new ArrayList<Field.TermVector>();
+ public final List<XMember> embeddedGetters = new ArrayList<XMember>();
+ public final List<PropertiesMetadata> embeddedPropertiesMetadata = new ArrayList<PropertiesMetadata>();
+ public final List<Container> embeddedContainers = new ArrayList<Container>();
+ public final List<XMember> containedInGetters = new ArrayList<XMember>();
+
+ public final List<String> classNames = new ArrayList<String>();
+ public final List<Store> classStores = new ArrayList<Store>();
+ public final List<Field.Index> classIndexes = new ArrayList<Field.Index>();
+ public final List<FieldBridge> classBridges = new ArrayList<FieldBridge>();
+ public final List<Field.TermVector> classTermVectors = new ArrayList<Field.TermVector>();
+ public final List<Float> classBoosts = new ArrayList<Float>();
+
+ public enum Container {
+ OBJECT,
+ COLLECTION,
+ MAP,
+ ARRAY
+ }
+
+ protected LuceneOptions getClassLuceneOptions(int i) {
+ return new LuceneOptionsImpl(
+ classStores.get( i ),
+ classIndexes.get( i ), classTermVectors.get( i ), classBoosts.get( i )
+ );
+ }
+
+ protected LuceneOptions getFieldLuceneOptions(int i, Object value) {
+ LuceneOptions options;
+ options = new LuceneOptionsImpl(
+ fieldStore.get( i ),
+ fieldIndex.get( i ),
+ fieldTermVectors.get( i ),
+ fieldBoosts.get( i ) * dynamicFieldBoosts.get( i ).defineBoost( value )
+ );
+ return options;
+ }
+
+ protected float getClassBoost(Object value) {
+ return boost * classBoostStrategy.defineBoost( value );
+ }
+ }
+}
Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java 2010-09-23 14:54:28 UTC (rev 20691)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java 2010-09-24 13:53:55 UTC (rev 20692)
@@ -24,54 +24,14 @@
package org.hibernate.search.engine;
import java.io.Serializable;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.search.Similarity;
-import org.slf4j.Logger;
-
-import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.annotations.common.reflection.ReflectionManager;
-import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
-import org.hibernate.annotations.common.reflection.XMember;
import org.hibernate.annotations.common.reflection.XProperty;
-import org.hibernate.annotations.common.util.StringHelper;
-import org.hibernate.search.SearchException;
-import org.hibernate.search.analyzer.Discriminator;
-import org.hibernate.search.annotations.AnalyzerDef;
-import org.hibernate.search.annotations.AnalyzerDefs;
-import org.hibernate.search.annotations.AnalyzerDiscriminator;
-import org.hibernate.search.annotations.Boost;
-import org.hibernate.search.annotations.ClassBridge;
-import org.hibernate.search.annotations.ClassBridges;
-import org.hibernate.search.annotations.ContainedIn;
-import org.hibernate.search.annotations.DynamicBoost;
-import org.hibernate.search.annotations.Index;
-import org.hibernate.search.annotations.IndexedEmbedded;
-import org.hibernate.search.annotations.Store;
-import org.hibernate.search.annotations.TermVector;
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.backend.WorkType;
-import org.hibernate.search.bridge.BridgeFactory;
-import org.hibernate.search.bridge.FieldBridge;
-import org.hibernate.search.bridge.LuceneOptions;
import org.hibernate.search.impl.ConfigContext;
-import org.hibernate.search.util.HibernateHelper;
-import org.hibernate.search.util.LoggerFactory;
-import org.hibernate.search.util.PassThroughAnalyzer;
-import org.hibernate.search.util.ClassLoaderHelper;
-import org.hibernate.search.util.ReflectionHelper;
-import org.hibernate.search.util.ScopedAnalyzer;
/**
* Set up and provide a manager for classes which are indexed via <code>@IndexedEmbedded</code>, but themselves do not
@@ -83,23 +43,7 @@
* @author Richard Hallier
* @author Hardy Ferentschik
*/
-public class DocumentBuilderContainedEntity<T> implements DocumentBuilder {
- private static final Logger log = LoggerFactory.make();
-
- protected final PropertiesMetadata metadata = new PropertiesMetadata();
- protected final XClass beanXClass;
- protected final Class<?> beanClass;
- protected Set<Class<?>> mappedSubclasses = new HashSet<Class<?>>();
- protected ReflectionManager reflectionManager; //available only during initialization and post-initialization
- protected int level = 0;
- protected int maxLevel = Integer.MAX_VALUE;
- protected final ScopedAnalyzer analyzer = new ScopedAnalyzer();
- protected Similarity similarity; //there is only 1 similarity per class hierarchy, and only 1 per index
- protected boolean isRoot;
- protected EntityState entityState;
- private Analyzer passThroughAnalyzer = new PassThroughAnalyzer();
- private boolean initialized;
-
+public class DocumentBuilderContainedEntity<T> extends AbstractDocumentBuilder<T> implements DocumentBuilder {
/**
* Constructor used on contained entities not annotated with <code>@Indexed</code> themselves.
*
@@ -108,512 +52,15 @@
* @param reflectionManager Reflection manager to use for processing the annotations.
*/
public DocumentBuilderContainedEntity(XClass xClass, ConfigContext context, ReflectionManager reflectionManager) {
-
- if ( xClass == null ) {
- throw new AssertionFailure( "Unable to build a DocumentBuilderContainedEntity with a null class" );
- }
-
- this.entityState = EntityState.CONTAINED_IN_ONLY;
- this.beanXClass = xClass;
- this.reflectionManager = reflectionManager;
- this.beanClass = reflectionManager.toClass( xClass );
- init( xClass, context );
-
- if ( metadata.containedInGetters.size() == 0 ) {
- this.entityState = EntityState.NON_INDEXABLE;
- }
+ super( xClass, context, reflectionManager );
}
- protected void init(XClass clazz, ConfigContext context) {
- metadata.boost = getBoost( clazz );
- metadata.classBoostStrategy = getDynamicBoost( clazz );
- metadata.analyzer = context.getDefaultAnalyzer();
-
- Set<XClass> processedClasses = new HashSet<XClass>();
- processedClasses.add( clazz );
- initializeClass( clazz, metadata, true, "", processedClasses, context );
-
- this.analyzer.setGlobalAnalyzer( metadata.analyzer );
-
- // set the default similarity in case that after processing all classes there is still no similarity set
- if ( this.similarity == null ) {
- this.similarity = context.getDefaultSimilarity();
- }
+ protected void initSubClass(XClass clazz, ConfigContext context) {
}
- public boolean isRoot() {
- return isRoot;
+ protected void subClassSpecificCheck(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, ConfigContext context) {
}
- private void initializeClass(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix,
- Set<XClass> processedClasses, ConfigContext context) {
- List<XClass> hierarchy = new ArrayList<XClass>();
- for ( XClass currentClass = clazz; currentClass != null; currentClass = currentClass.getSuperclass() ) {
- hierarchy.add( currentClass );
- }
-
- /*
- * Iterate the class hierarchy top down. This allows to override the default analyzer for the properties if the class holds one
- */
- for ( int index = hierarchy.size() - 1; index >= 0; index-- ) {
- XClass currentClass = hierarchy.get( index );
-
- initializeClassLevelAnnotations( currentClass, propertiesMetadata, isRoot, prefix, context );
-
- // rejecting non properties (ie regular methods) because the object is loaded from Hibernate,
- // so indexing a non property does not make sense
- List<XProperty> methods = currentClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
- for ( XProperty method : methods ) {
- initializeMemberLevelAnnotations(
- method, propertiesMetadata, isRoot, prefix, processedClasses, context
- );
- }
-
- List<XProperty> fields = currentClass.getDeclaredProperties( XClass.ACCESS_FIELD );
- for ( XProperty field : fields ) {
- initializeMemberLevelAnnotations(
- field, propertiesMetadata, isRoot, prefix, processedClasses, context
- );
- }
- }
- }
-
- /**
- * Check and initialize class level annotations.
- *
- * @param clazz The class to process.
- * @param propertiesMetadata The meta data holder.
- * @param isRoot Flag indicating if the specified class is a root entity, meaning the start of a chain of indexed
- * entities.
- * @param prefix The current prefix used for the <code>Document</code> field names.
- * @param context Handle to default configuration settings.
- */
- private void initializeClassLevelAnnotations(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, ConfigContext context) {
-
- // check for a class level specified analyzer
- Analyzer analyzer = getAnalyzer( clazz, context );
- if ( analyzer != null ) {
- propertiesMetadata.analyzer = analyzer;
- }
-
- // check for AnalyzerDefs annotations
- checkForAnalyzerDefs( clazz, context );
-
- // Check for any ClassBridges annotation.
- ClassBridges classBridgesAnn = clazz.getAnnotation( ClassBridges.class );
- if ( classBridgesAnn != null ) {
- ClassBridge[] classBridges = classBridgesAnn.value();
- for ( ClassBridge cb : classBridges ) {
- bindClassBridgeAnnotation( prefix, propertiesMetadata, cb, context );
- }
- }
-
- // Check for any ClassBridge style of annotations.
- ClassBridge classBridgeAnn = clazz.getAnnotation( ClassBridge.class );
- if ( classBridgeAnn != null ) {
- bindClassBridgeAnnotation( prefix, propertiesMetadata, classBridgeAnn, context );
- }
-
- checkForAnalyzerDiscriminator( clazz, propertiesMetadata );
-
- // Get similarity
- if ( isRoot ) {
- checkForSimilarity( clazz );
- }
- }
-
- protected void initializeMemberLevelAnnotations(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot,
- String prefix, Set<XClass> processedClasses, ConfigContext context) {
- checkDocumentId( member, propertiesMetadata, isRoot, prefix, context );
- checkForField( member, propertiesMetadata, prefix, context );
- checkForFields( member, propertiesMetadata, prefix, context );
- checkForAnalyzerDefs( member, context );
- checkForAnalyzerDiscriminator( member, propertiesMetadata );
- checkForIndexedEmbedded( member, propertiesMetadata, prefix, processedClasses, context );
- checkForContainedIn( member, propertiesMetadata );
- }
-
- protected Analyzer getAnalyzer(XAnnotatedElement annotatedElement, ConfigContext context) {
- org.hibernate.search.annotations.Analyzer analyzerAnn =
- annotatedElement.getAnnotation( org.hibernate.search.annotations.Analyzer.class );
- return getAnalyzer( analyzerAnn, context );
- }
-
- protected Analyzer getAnalyzer(org.hibernate.search.annotations.Analyzer analyzerAnn, ConfigContext context) {
- Class<?> analyzerClass = analyzerAnn == null ? void.class : analyzerAnn.impl();
- if ( analyzerClass == void.class ) {
- String definition = analyzerAnn == null ? "" : analyzerAnn.definition();
- if ( StringHelper.isEmpty( definition ) ) {
- return null;
- }
- else {
- return context.buildLazyAnalyzer( definition );
- }
- }
- else {
- try {
- return ClassLoaderHelper.analyzerInstanceFromClass( analyzerClass, context.getLuceneMatchVersion() );
- }
- catch ( ClassCastException e ) {
- throw new SearchException(
- "Lucene analyzer does not implement " + Analyzer.class.getName() + ": " + analyzerClass.getName(),
- e
- );
- }
- catch ( Exception e ) {
- throw new SearchException(
- "Failed to instantiate lucene analyzer with type " + analyzerClass.getName(), e
- );
- }
- }
- }
-
- private void checkForAnalyzerDefs(XAnnotatedElement annotatedElement, ConfigContext context) {
- AnalyzerDefs defs = annotatedElement.getAnnotation( AnalyzerDefs.class );
- if ( defs != null ) {
- for ( AnalyzerDef def : defs.value() ) {
- context.addAnalyzerDef( def );
- }
- }
- AnalyzerDef def = annotatedElement.getAnnotation( AnalyzerDef.class );
- context.addAnalyzerDef( def );
- }
-
- private void checkForAnalyzerDiscriminator(XAnnotatedElement annotatedElement, PropertiesMetadata propertiesMetadata) {
- AnalyzerDiscriminator discriminatorAnn = annotatedElement.getAnnotation( AnalyzerDiscriminator.class );
- if ( discriminatorAnn != null ) {
- if ( propertiesMetadata.discriminator != null ) {
- throw new SearchException(
- "Multiple AnalyzerDiscriminator defined in the same class hierarchy: " + beanXClass.getName()
- );
- }
-
- Class<? extends Discriminator> discriminatorClass = discriminatorAnn.impl();
- try {
- propertiesMetadata.discriminator = discriminatorClass.newInstance();
- }
- catch ( Exception e ) {
- throw new SearchException(
- "Unable to instantiate analyzer discriminator implementation: " + discriminatorClass.getName()
- );
- }
-
- if ( annotatedElement instanceof XMember ) {
- propertiesMetadata.discriminatorGetter = ( XMember ) annotatedElement;
- }
- }
- }
-
- public Similarity getSimilarity() {
- return similarity;
- }
-
- private void checkForFields(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, ConfigContext context) {
- org.hibernate.search.annotations.Fields fieldsAnn =
- member.getAnnotation( org.hibernate.search.annotations.Fields.class );
- if ( fieldsAnn != null ) {
- for ( org.hibernate.search.annotations.Field fieldAnn : fieldsAnn.value() ) {
- bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
- }
- }
- }
-
- private void checkForSimilarity(XClass currClass) {
- org.hibernate.search.annotations.Similarity similarityAnn = currClass.getAnnotation( org.hibernate.search.annotations.Similarity.class );
- if ( similarityAnn != null ) {
- if ( similarity != null ) {
- throw new SearchException(
- "Multiple Similarities defined in the same class hierarchy: " + beanXClass.getName()
- );
- }
- Class<?> similarityClass = similarityAnn.impl();
- try {
- similarity = ( Similarity ) similarityClass.newInstance();
- }
- catch ( Exception e ) {
- log.error(
- "Exception attempting to instantiate Similarity '{}' set for {}",
- similarityClass.getName(), beanXClass.getName()
- );
- }
- }
- }
-
- private void checkForField(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, ConfigContext context) {
- org.hibernate.search.annotations.Field fieldAnn =
- member.getAnnotation( org.hibernate.search.annotations.Field.class );
- if ( fieldAnn != null ) {
- bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
- }
- }
-
- private void checkForContainedIn(XProperty member, PropertiesMetadata propertiesMetadata) {
- ContainedIn containedAnn = member.getAnnotation( ContainedIn.class );
- if ( containedAnn != null ) {
- ReflectionHelper.setAccessible( member );
- propertiesMetadata.containedInGetters.add( member );
- }
- }
-
- private void checkForIndexedEmbedded(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, Set<XClass> processedClasses, ConfigContext context) {
- IndexedEmbedded embeddedAnn = member.getAnnotation( IndexedEmbedded.class );
- if ( embeddedAnn != null ) {
- int oldMaxLevel = maxLevel;
- int potentialLevel = embeddedAnn.depth() + level;
- if ( potentialLevel < 0 ) {
- potentialLevel = Integer.MAX_VALUE;
- }
- maxLevel = potentialLevel > maxLevel ? maxLevel : potentialLevel;
- level++;
-
- XClass elementClass;
- if ( void.class == embeddedAnn.targetElement() ) {
- elementClass = member.getElementClass();
- }
- else {
- elementClass = reflectionManager.toXClass( embeddedAnn.targetElement() );
- }
- if ( maxLevel == Integer.MAX_VALUE //infinite
- && processedClasses.contains( elementClass ) ) {
- throw new SearchException(
- "Circular reference. Duplicate use of "
- + elementClass.getName()
- + " in root entity " + beanXClass.getName()
- + "#" + buildEmbeddedPrefix( prefix, embeddedAnn, member )
- );
- }
- if ( level <= maxLevel ) {
- processedClasses.add( elementClass ); //push
-
- ReflectionHelper.setAccessible( member );
- propertiesMetadata.embeddedGetters.add( member );
- PropertiesMetadata metadata = new PropertiesMetadata();
- propertiesMetadata.embeddedPropertiesMetadata.add( metadata );
- metadata.boost = getBoost( member, null );
- //property > entity analyzer
- Analyzer analyzer = getAnalyzer( member, context );
- metadata.analyzer = analyzer != null ? analyzer : propertiesMetadata.analyzer;
- String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
- initializeClass( elementClass, metadata, false, localPrefix, processedClasses, context );
- /**
- * We will only index the "expected" type but that's OK, HQL cannot do down-casting either
- */
- if ( member.isArray() ) {
- propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.ARRAY );
- }
- else if ( member.isCollection() ) {
- if ( Map.class.equals( member.getCollectionClass() ) ) {
- //hum subclasses etc etc??
- propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.MAP );
- }
- else {
- propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.COLLECTION );
- }
- }
- else {
- propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.OBJECT );
- }
-
- processedClasses.remove( elementClass ); //pop
- }
- else if ( log.isTraceEnabled() ) {
- String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
- log.trace( "depth reached, ignoring {}", localPrefix );
- }
-
- level--;
- maxLevel = oldMaxLevel; //set back the the old max level
- }
- }
-
- protected void checkDocumentId(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, ConfigContext context) {
- // TODO - HSEARCH-333
- // for a contained entity there is nothing to do here. This is really bad design since this protected method is called by the constructor and
- // overridden by DocumentBuilderIndexedEntity
- }
-
- /**
- * Determines the property name for the document id. It is either the name of the property itself or the
- * value of the name attribute of the <code>idAnnotation</code>.
- *
- * @param member the property used as id property.
- * @param idAnnotation the id annotation
- *
- * @return property name to be used as document id.
- */
- protected String getIdAttributeName(XProperty member, Annotation idAnnotation) {
- String name = null;
- try {
- Method m = idAnnotation.getClass().getMethod( "name" );
- name = ( String ) m.invoke( idAnnotation );
- }
- catch ( Exception e ) {
- // ignore
- }
-
- return ReflectionHelper.getAttributeName( member, name );
- }
-
- private void bindClassBridgeAnnotation(String prefix, PropertiesMetadata propertiesMetadata, ClassBridge ann, ConfigContext context) {
- String fieldName = prefix + ann.name();
- propertiesMetadata.classNames.add( fieldName );
- propertiesMetadata.classStores.add( ann.store() );
- propertiesMetadata.classIndexes.add( getIndex( ann.index() ) );
- propertiesMetadata.classTermVectors.add( getTermVector( ann.termVector() ) );
- propertiesMetadata.classBridges.add( BridgeFactory.extractType( ann ) );
- propertiesMetadata.classBoosts.add( ann.boost().value() );
-
- Analyzer analyzer = getAnalyzer( ann.analyzer(), context );
- if ( analyzer == null ) {
- analyzer = propertiesMetadata.analyzer;
- }
- if ( analyzer == null ) {
- throw new AssertionFailure( "Analyzer should not be undefined" );
- }
- addToScopedAnalyzer( fieldName, analyzer, ann.index() );
- }
-
- private void bindFieldAnnotation(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, org.hibernate.search.annotations.Field fieldAnn, ConfigContext context) {
- ReflectionHelper.setAccessible( member );
- propertiesMetadata.fieldGetters.add( member );
- String fieldName = prefix + ReflectionHelper.getAttributeName( member, fieldAnn.name() );
- propertiesMetadata.fieldNames.add( fieldName );
- propertiesMetadata.fieldStore.add( fieldAnn.store() );
- propertiesMetadata.fieldIndex.add( getIndex( fieldAnn.index() ) );
- propertiesMetadata.fieldBoosts.add( getBoost( member, fieldAnn ) );
- propertiesMetadata.dynamicFieldBoosts.add( getDynamicBoost( member ) );
- propertiesMetadata.fieldTermVectors.add( getTermVector( fieldAnn.termVector() ) );
- propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( fieldAnn, member, reflectionManager ) );
-
- // Field > property > entity analyzer
- Analyzer analyzer = getAnalyzer( fieldAnn.analyzer(), context );
- if ( analyzer == null ) {
- analyzer = getAnalyzer( member, context );
- }
- addToScopedAnalyzer( fieldName, analyzer, fieldAnn.index() );
- }
-
- protected void addToScopedAnalyzer(String fieldName, Analyzer analyzer, Index index) {
- if ( index == Index.TOKENIZED) {
- if ( analyzer != null ) {
- this.analyzer.addScopedAnalyzer( fieldName, analyzer );
- }
- }
- else {
- //no analyzer is used, add a fake one for queries
- this.analyzer.addScopedAnalyzer( fieldName, passThroughAnalyzer );
- }
- }
-
- protected Float getBoost(XProperty member, org.hibernate.search.annotations.Field fieldAnn) {
- float computedBoost = 1.0f;
- Boost boostAnn = member.getAnnotation( Boost.class );
- if ( boostAnn != null ) {
- computedBoost = boostAnn.value();
- }
- if ( fieldAnn != null ) {
- computedBoost *= fieldAnn.boost().value();
- }
- return computedBoost;
- }
-
- protected BoostStrategy getDynamicBoost(XProperty member) {
- DynamicBoost boostAnnotation = member.getAnnotation( DynamicBoost.class );
- if ( boostAnnotation == null ) {
- return new DefaultBoostStrategy();
- }
-
- Class<? extends BoostStrategy> boostStrategyClass = boostAnnotation.impl();
- BoostStrategy strategy;
- try {
- strategy = boostStrategyClass.newInstance();
- }
- catch ( Exception e ) {
- throw new SearchException(
- "Unable to instantiate boost strategy implementation: " + boostStrategyClass.getName()
- );
- }
- return strategy;
- }
-
- private String buildEmbeddedPrefix(String prefix, IndexedEmbedded embeddedAnn, XProperty member) {
- String localPrefix = prefix;
- if ( ".".equals( embeddedAnn.prefix() ) ) {
- //default to property name
- localPrefix += member.getName() + '.';
- }
- else {
- localPrefix += embeddedAnn.prefix();
- }
- return localPrefix;
- }
-
- protected Field.TermVector getTermVector(TermVector vector) {
- switch ( vector ) {
- case NO:
- return Field.TermVector.NO;
- case YES:
- return Field.TermVector.YES;
- case WITH_OFFSETS:
- return Field.TermVector.WITH_OFFSETS;
- case WITH_POSITIONS:
- return Field.TermVector.WITH_POSITIONS;
- case WITH_POSITION_OFFSETS:
- return Field.TermVector.WITH_POSITIONS_OFFSETS;
- default:
- throw new AssertionFailure( "Unexpected TermVector: " + vector );
- }
- }
-
- protected Field.Index getIndex(Index index) {
- switch ( index ) {
- case NO:
- return Field.Index.NO;
- case NO_NORMS:
- return Field.Index.NOT_ANALYZED_NO_NORMS;
- case TOKENIZED:
- return Field.Index.ANALYZED;
- case UN_TOKENIZED:
- return Field.Index.NOT_ANALYZED;
- default:
- throw new AssertionFailure( "Unexpected Index: " + index );
- }
- }
-
- protected float getBoost(XClass element) {
- float boost = 1.0f;
- if ( element == null ) {
- return boost;
- }
- Boost boostAnnotation = element.getAnnotation( Boost.class );
- if ( boostAnnotation != null ) {
- boost = boostAnnotation.value();
- }
- return boost;
- }
-
- protected BoostStrategy getDynamicBoost(XClass element) {
- if ( element == null ) {
- return null;
- }
- DynamicBoost boostAnnotation = element.getAnnotation( DynamicBoost.class );
- if ( boostAnnotation == null ) {
- return new DefaultBoostStrategy();
- }
-
- Class<? extends BoostStrategy> boostStrategyClass = boostAnnotation.impl();
- BoostStrategy strategy;
- try {
- strategy = boostStrategyClass.newInstance();
- }
- catch ( Exception e ) {
- throw new SearchException(
- "Unable to instantiate boost strategy implementation: " + boostStrategyClass.getName()
- );
- }
- return strategy;
- }
-
public void addWorkToQueue(Class<T> entityClass, T entity, Serializable id, WorkType workType, List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor) {
/**
* When references are changed, either null or another one, we expect dirty checking to be triggered (both sides
@@ -624,224 +71,4 @@
processContainedInInstances( entity, queue, metadata, searchFactoryImplementor );
}
}
-
- /**
- * If we have a work instance we have to check whether the instance to be indexed is contained in any other indexed entities.
- *
- * @param instance The instance to be indexed
- * @param queue the current work queue
- * @param metadata metadata
- * @param searchFactoryImplementor the current session
- */
- private <T> void processContainedInInstances(Object instance, List<LuceneWork> queue, PropertiesMetadata metadata, SearchFactoryImplementor searchFactoryImplementor) {
- for ( int i = 0; i < metadata.containedInGetters.size(); i++ ) {
- XMember member = metadata.containedInGetters.get( i );
- Object value = ReflectionHelper.getMemberValue( instance, member );
-
- if ( value == null ) {
- continue;
- }
-
- if ( member.isArray() ) {
- @SuppressWarnings("unchecked")
- T[] array = ( T[] ) value;
- for ( T arrayValue : array ) {
- processSingleContainedInInstance( queue, searchFactoryImplementor, arrayValue );
- }
- }
- else if ( member.isCollection() ) {
- Collection<T> collection = null;
- try {
- collection = getActualCollection( member, value );
- collection.size(); //load it
- }
- catch ( Exception e ) {
- if ( e.getClass().getName().contains( "org.hibernate.LazyInitializationException" ) ) {
- /* A deleted entity not having its collection initialized
- * leads to a LIE because the colleciton is no longer attached to the session
- *
- * But that's ok as the collection update event has been processed before
- * or the fk would have been cleared and thus triggering the cleaning
- */
- collection = null;
- }
- }
- if ( collection != null ) {
- for ( T collectionValue : collection ) {
- processSingleContainedInInstance( queue, searchFactoryImplementor, collectionValue );
- }
- }
- }
- else {
- processSingleContainedInInstance( queue, searchFactoryImplementor, value );
- }
- }
- }
-
- /**
- * A {@code XMember } instance treats a map as a collection as well in which case the map values are returned as
- * collection.
- *
- * @param member The member instance
- * @param value The value
- *
- * @return The {@code value} casted to collection or in case of {@code value} being a map the map values as collection.
- */
- private <T> Collection<T> getActualCollection(XMember member, Object value) {
- Collection<T> collection;
- if ( Map.class.equals( member.getCollectionClass() ) ) {
- //hum
- @SuppressWarnings("unchecked")
- Collection<T> tmpCollection = ( ( Map<?, T> ) value ).values();
- collection = tmpCollection;
- }
- else {
- @SuppressWarnings("unchecked")
- Collection<T> tmpCollection = ( Collection<T> ) value;
- collection = tmpCollection;
- }
- return collection;
- }
-
- private <T> void processSingleContainedInInstance(List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor, T value) {
- Class<T> valueClass = HibernateHelper.getClass( value );
- DocumentBuilderIndexedEntity<T> builderIndexedEntity =
- searchFactoryImplementor.getDocumentBuilderIndexedEntity( valueClass );
-
- // it could be we have a nested @IndexedEmbedded chain in which case we have to find the top level @Indexed entities
- if ( builderIndexedEntity == null ) {
- DocumentBuilderContainedEntity<T> builderContainedEntity =
- searchFactoryImplementor.getDocumentBuilderContainedEntity( valueClass );
- if ( builderContainedEntity != null ) {
- processContainedInInstances( value, queue, builderContainedEntity.metadata, searchFactoryImplementor );
- }
- }
- else {
- addWorkForEmbeddedValue( value, queue, valueClass, builderIndexedEntity, searchFactoryImplementor );
- }
- }
-
- /**
- * Create a {@code LuceneWork} instance of the entity which needs updating due to the embedded instance change.
- *
- * @param value The value to index
- * @param queue The current (Lucene) work queue
- * @param valueClass The class of the value
- * @param builderIndexedEntity the document builder for the entity which needs updating due to a update event of the embedded instance
- * @param searchFactoryImplementor the search factory.
- */
- private <T> void addWorkForEmbeddedValue(T value, List<LuceneWork> queue, Class<T> valueClass,
- DocumentBuilderIndexedEntity<T> builderIndexedEntity, SearchFactoryImplementor searchFactoryImplementor) {
- Serializable id = ( Serializable ) ReflectionHelper.getMemberValue( value, builderIndexedEntity.idGetter );
- if ( id != null) {
- builderIndexedEntity.addWorkToQueue( valueClass, value, id, WorkType.UPDATE, queue, searchFactoryImplementor );
- }
- else {
- //this is an indexed entity that is not yet persisted but should be reached by cascade
- // and thus raise an Hibernate Core event leading to its indexing by Hibernate Search
- // => no need to do anything here
- }
- }
-
- public Analyzer getAnalyzer() {
- return analyzer;
- }
-
- public void postInitialize(Set<Class<?>> indexedClasses) {
- //we initialize only once because we no longer have a reference to the reflectionManager
- //in theory
- Class<?> plainClass = beanClass;
- if ( entityState == EntityState.NON_INDEXABLE ) {
- throw new AssertionFailure( "A non indexed entity is post processed" );
- }
- Set<Class<?>> tempMappedSubclasses = new HashSet<Class<?>>();
- //together with the caller this creates a o(2), but I think it's still faster than create the up hierarchy for each class
- for ( Class<?> currentClass : indexedClasses ) {
- if ( plainClass != currentClass && plainClass.isAssignableFrom( currentClass ) ) {
- tempMappedSubclasses.add( currentClass );
- }
- }
- this.mappedSubclasses = Collections.unmodifiableSet( tempMappedSubclasses );
- Class<?> superClass = plainClass.getSuperclass();
- this.isRoot = true;
- while ( superClass != null ) {
- if ( indexedClasses.contains( superClass ) ) {
- this.isRoot = false;
- break;
- }
- superClass = superClass.getSuperclass();
- }
- this.reflectionManager = null;
- }
-
- public EntityState getEntityState() {
- return entityState;
- }
-
- public Set<Class<?>> getMappedSubclasses() {
- return mappedSubclasses;
- }
-
- /**
- * Wrapper class containing all the meta data extracted out of a single entity.
- * All field/property related properties are kept in lists. Retrieving all metadata for a given
- * property/field means accessing all the lists with the same index.
- */
- protected static class PropertiesMetadata {
- public float boost;
- public Analyzer analyzer;
- public Discriminator discriminator;
- public XMember discriminatorGetter;
- public BoostStrategy classBoostStrategy;
-
- public final List<String> fieldNames = new ArrayList<String>();
- public final List<XMember> fieldGetters = new ArrayList<XMember>();
- public final List<FieldBridge> fieldBridges = new ArrayList<FieldBridge>();
- public final List<Store> fieldStore = new ArrayList<Store>();
- public final List<Field.Index> fieldIndex = new ArrayList<Field.Index>();
- public final List<Float> fieldBoosts = new ArrayList<Float>();
- public final List<BoostStrategy> dynamicFieldBoosts = new ArrayList<BoostStrategy>();
-
- public final List<Field.TermVector> fieldTermVectors = new ArrayList<Field.TermVector>();
- public final List<XMember> embeddedGetters = new ArrayList<XMember>();
- public final List<PropertiesMetadata> embeddedPropertiesMetadata = new ArrayList<PropertiesMetadata>();
- public final List<Container> embeddedContainers = new ArrayList<Container>();
- public final List<XMember> containedInGetters = new ArrayList<XMember>();
-
- public final List<String> classNames = new ArrayList<String>();
- public final List<Store> classStores = new ArrayList<Store>();
- public final List<Field.Index> classIndexes = new ArrayList<Field.Index>();
- public final List<FieldBridge> classBridges = new ArrayList<FieldBridge>();
- public final List<Field.TermVector> classTermVectors = new ArrayList<Field.TermVector>();
- public final List<Float> classBoosts = new ArrayList<Float>();
-
- public enum Container {
- OBJECT,
- COLLECTION,
- MAP,
- ARRAY
- }
-
- protected LuceneOptions getClassLuceneOptions(int i) {
- return new LuceneOptionsImpl(
- classStores.get( i ),
- classIndexes.get( i ), classTermVectors.get( i ), classBoosts.get( i )
- );
- }
-
- protected LuceneOptions getFieldLuceneOptions(int i, Object value) {
- LuceneOptions options;
- options = new LuceneOptionsImpl(
- fieldStore.get( i ),
- fieldIndex.get( i ),
- fieldTermVectors.get( i ),
- fieldBoosts.get( i ) * dynamicFieldBoosts.get( i ).defineBoost( value )
- );
- return options;
- }
-
- protected float getClassBoost(Object value) {
- return boost * classBoostStrategy.defineBoost( value );
- }
- }
}
Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/DocumentBuilderIndexedEntity.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/DocumentBuilderIndexedEntity.java 2010-09-23 14:54:28 UTC (rev 20691)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/engine/DocumentBuilderIndexedEntity.java 2010-09-24 13:53:55 UTC (rev 20692)
@@ -26,6 +26,7 @@
import java.io.Serializable;
import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -47,7 +48,6 @@
import org.hibernate.annotations.common.reflection.XMember;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.annotations.common.util.ReflectHelper;
-import org.hibernate.proxy.HibernateProxy;
import org.hibernate.search.SearchException;
import org.hibernate.search.analyzer.Discriminator;
import org.hibernate.search.annotations.DocumentId;
@@ -83,7 +83,7 @@
* @author Richard Hallier
* @author Hardy Ferentschik
*/
-public class DocumentBuilderIndexedEntity<T> extends DocumentBuilderContainedEntity<T> {
+public class DocumentBuilderIndexedEntity<T> extends AbstractDocumentBuilder<T> {
private static final Logger log = LoggerFactory.make();
/**
@@ -153,9 +153,7 @@
this.shardingStrategy = shardingStrategy;
}
- protected void init(XClass clazz, ConfigContext context) {
- super.init( clazz, context );
-
+ protected void initSubClass(XClass clazz, ConfigContext context) {
// special case @ProvidedId
ProvidedId provided = findProvidedId( clazz, reflectionManager );
if ( provided != null ) {
@@ -177,6 +175,10 @@
}
}
+ protected void subClassSpecificCheck(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, ConfigContext context) {
+ checkDocumentId( member, propertiesMetadata, isRoot, prefix, context );
+ }
+
protected void checkDocumentId(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, ConfigContext context) {
Annotation idAnnotation = getIdAnnotation( member, context );
if ( idAnnotation != null ) {
@@ -278,8 +280,6 @@
return id;
}
- //TODO could we use T instead of EntityClass?
-
public void addWorkToQueue(Class<T> entityClass, T entity, Serializable id, WorkType workType, List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor) {
//TODO with the caller loop we are in a n^2: optimize it using a HashMap for work recognition
@@ -358,7 +358,9 @@
throw new AssertionFailure( "Unknown WorkType: " + workType );
}
- super.addWorkToQueue( entityClass, entity, id, workType, queue, searchFactoryImplementor );
+ if ( workType.searchForContainers() ) {
+ processContainedInInstances( entity, queue, metadata, searchFactoryImplementor );
+ }
}
public AddLuceneWork createAddWork(Class<T> entityClass, T entity, Serializable id, String idInString, boolean isBatch) {
@@ -424,7 +426,7 @@
}
// needed for field access: I cannot work in the proxied version
- Object unproxiedInstance = unproxy( instance );
+ Object unproxiedInstance = HibernateHelper.unproxy( instance );
// process the class bridges
for ( int i = 0; i < propertiesMetadata.classBridges.size(); i++ ) {
@@ -528,16 +530,6 @@
}
}
- private Object unproxy(Object value) {
- //FIXME this service should be part of Core?
- if ( value instanceof HibernateProxy ) {
- // .getImplementation() initializes the data by side effect
- value = ( ( HibernateProxy ) value ).getHibernateLazyInitializer()
- .getImplementation();
- }
- return value;
- }
-
public String getIdentifierName() {
return idGetter.getName();
}
@@ -594,6 +586,8 @@
* <p/>
* If the id is provided, we can't extract it from the entity
*
+ * @param entity The entity for which to return the id. Cannot be {@code null}.
+ *
* @return entity id
*/
public Serializable getId(Object entity) {
@@ -804,4 +798,26 @@
}
}
}
+
+ /**
+ * Determines the property name for the document id. It is either the name of the property itself or the
+ * value of the name attribute of the <code>idAnnotation</code>.
+ *
+ * @param member the property used as id property.
+ * @param idAnnotation the id annotation
+ *
+ * @return property name to be used as document id.
+ */
+ private String getIdAttributeName(XProperty member, Annotation idAnnotation) {
+ String name = null;
+ try {
+ Method m = idAnnotation.getClass().getMethod( "name" );
+ name = ( String ) m.invoke( idAnnotation );
+ }
+ catch ( Exception e ) {
+ // ignore
+ }
+
+ return ReflectionHelper.getAttributeName( member, name );
+ }
}
Modified: search/trunk/hibernate-search/src/main/java/org/hibernate/search/util/HibernateHelper.java
===================================================================
--- search/trunk/hibernate-search/src/main/java/org/hibernate/search/util/HibernateHelper.java 2010-09-23 14:54:28 UTC (rev 20691)
+++ search/trunk/hibernate-search/src/main/java/org/hibernate/search/util/HibernateHelper.java 2010-09-24 13:53:55 UTC (rev 20692)
@@ -25,6 +25,7 @@
package org.hibernate.search.util;
import org.hibernate.Hibernate;
+import org.hibernate.proxy.HibernateProxy;
import org.hibernate.search.backend.Work;
/**
@@ -39,9 +40,7 @@
* In case of Hibernate proxies, return the entity type rather than the proxy's
*/
public static <T> Class<T> getClass(T entity) {
- @SuppressWarnings("unchecked")
- final Class<T> type = Hibernate.getClass( entity );
- return type;
+ return ( Class<T> ) Hibernate.getClass( entity );
}
public static void initialize(Object entity) {
@@ -57,4 +56,11 @@
work.getEntityClass() :
getClass( work.getEntity() );
}
+
+ public static Object unproxy(Object value) {
+ if ( value instanceof HibernateProxy ) {
+ value = ( ( HibernateProxy ) value ).getHibernateLazyInitializer().getImplementation();
+ }
+ return value;
+ }
}
More information about the hibernate-commits
mailing list