org.hibernate.search.engine.DocumentBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-annotations
Show all versions of hibernate-annotations
Annotations metadata for Hibernate
//$Id: DocumentBuilder.java 10865 2006-11-23 23:30:01 +0100 (jeu., 23 nov. 2006) epbernard $
package org.hibernate.search.engine;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.Term;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.cfg.annotations.Version;
import org.hibernate.search.annotations.Boost;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Keyword;
import org.hibernate.search.annotations.Store;
import org.hibernate.search.annotations.Text;
import org.hibernate.search.annotations.Unstored;
import org.hibernate.search.bridge.BridgeFactory;
import org.hibernate.search.bridge.FieldBridge;
import org.hibernate.search.bridge.TwoWayFieldBridge;
import org.hibernate.search.event.FullTextIndexEventListener;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.util.BinderHelper;
import org.hibernate.reflection.ReflectionManager;
import org.hibernate.reflection.XAnnotatedElement;
import org.hibernate.reflection.XClass;
import org.hibernate.reflection.XMember;
import org.hibernate.reflection.XProperty;
import org.hibernate.util.ReflectHelper;
/**
* Set up and provide a manager for indexes classes
*
* @author Gavin King
* @author Emmanuel Bernard
* @author Sylvain Vieujot
* @author Richard Hallier
*/
public class DocumentBuilder {
static {
Version.touch(); //touch version
}
private final List keywordGetters = new ArrayList();
private final List keywordNames = new ArrayList();
private final List keywordBridges = new ArrayList();
private final List unstoredGetters = new ArrayList();
private final List unstoredNames = new ArrayList();
private final List unstoredBridges = new ArrayList();
private final List textGetters = new ArrayList();
private final List textNames = new ArrayList();
private final List textBridges = new ArrayList();
private final List fieldNames = new ArrayList();
private final List fieldGetters = new ArrayList();
private final List fieldBridges = new ArrayList();
private final List fieldStore = new ArrayList();
private final List fieldIndex = new ArrayList();
private final XClass beanClass;
private final DirectoryProvider directoryProvider;
private String idKeywordName;
private final Analyzer analyzer;
private Float idBoost;
public static final String CLASS_FIELDNAME = "_hibernate_class";
private TwoWayFieldBridge idBridge;
private Set mappedSubclasses = new HashSet();
private ReflectionManager reflectionManager;
public DocumentBuilder(XClass clazz, Analyzer analyzer, DirectoryProvider directory,
ReflectionManager reflectionManager) {
this.beanClass = clazz;
this.analyzer = analyzer;
this.directoryProvider = directory;
//FIXME get rid of it when boost is stored?
this.reflectionManager = reflectionManager;
if ( clazz == null ) throw new AssertionFailure( "Unable to build a DocumentBuilder with a null class" );
for ( XClass currClass = beanClass; currClass != null; currClass = currClass.getSuperclass() ) {
//rejecting non properties because the object is loaded from Hibernate, so indexing a non property does not make sense
List methods = currClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
for ( XProperty method : methods ) {
initializeMember( method );
}
List fields = currClass.getDeclaredProperties( XClass.ACCESS_FIELD );
for ( XProperty field : fields ) {
initializeMember( field );
}
}
if ( idKeywordName == null ) {
throw new HibernateException( "No document id for: " + clazz.getName() );
}
}
private void initializeMember(XProperty member) {
Keyword keywordAnn = member.getAnnotation( Keyword.class );
if ( keywordAnn != null ) {
String name = BinderHelper.getAttributeName( member, keywordAnn.name() );
if ( keywordAnn.id() ) {
idKeywordName = name;
idBoost = getBoost( member );
FieldBridge fieldBridge = BridgeFactory.guessType( member );
if ( fieldBridge instanceof TwoWayFieldBridge ) {
idBridge = (TwoWayFieldBridge) fieldBridge;
}
else {
throw new HibernateException(
"Bridge for document id does not implement IdFieldBridge: " + member.getName() );
}
}
else {
setAccessible( member );
keywordGetters.add( member );
keywordNames.add( name );
keywordBridges.add( BridgeFactory.guessType( member ) );
}
}
Unstored unstoredAnn = member.getAnnotation( Unstored.class );
if ( unstoredAnn != null ) {
setAccessible( member );
unstoredGetters.add( member );
unstoredNames.add( BinderHelper.getAttributeName( member, unstoredAnn.name() ) );
unstoredBridges.add( BridgeFactory.guessType( member ) );
}
Text textAnn = member.getAnnotation( Text.class );
if ( textAnn != null ) {
setAccessible( member );
textGetters.add( member );
textNames.add( BinderHelper.getAttributeName( member, textAnn.name() ) );
textBridges.add( BridgeFactory.guessType( member ) );
}
DocumentId documentIdAnn = member.getAnnotation( DocumentId.class );
if ( documentIdAnn != null ) {
if ( idKeywordName != null ) {
throw new AssertionFailure( "Two document id assigned: "
+ idKeywordName + " and " + BinderHelper.getAttributeName( member, documentIdAnn.name() ) );
}
idKeywordName = BinderHelper.getAttributeName( member, documentIdAnn.name() );
FieldBridge fieldBridge = BridgeFactory.guessType( member );
if ( fieldBridge instanceof TwoWayFieldBridge ) {
idBridge = (TwoWayFieldBridge) fieldBridge;
}
else {
throw new HibernateException(
"Bridge for document id does not implement IdFieldBridge: " + member.getName() );
}
idBoost = getBoost( member );
}
org.hibernate.search.annotations.Field fieldAnn =
member.getAnnotation( org.hibernate.search.annotations.Field.class );
if ( fieldAnn != null ) {
setAccessible( member );
fieldGetters.add( member );
fieldNames.add( BinderHelper.getAttributeName( member, fieldAnn.name() ) );
fieldStore.add( getStore( fieldAnn.store() ) );
fieldIndex.add( getIndex( fieldAnn.index() ) );
fieldBridges.add( BridgeFactory.guessType( member ) );
}
}
private Field.Store getStore(Store store) {
switch (store) {
case NO:
return Field.Store.NO;
case YES:
return Field.Store.YES;
case COMPRESS:
return Field.Store.COMPRESS;
default:
throw new AssertionFailure( "Unexpected Store: " + store );
}
}
private Field.Index getIndex(Index index) {
switch (index) {
case NO:
return Field.Index.NO;
case NO_NORMS:
return Field.Index.NO_NORMS;
case TOKENIZED:
return Field.Index.TOKENIZED;
case UN_TOKENISED:
return Field.Index.UN_TOKENIZED;
default:
throw new AssertionFailure( "Unexpected Index: " + index );
}
}
private Float getBoost(XAnnotatedElement element) {
if ( element == null ) return null;
Boost boost = element.getAnnotation( Boost.class );
return boost != null ?
boost.value() :
null;
}
private Object getMemberValue(T bean, XMember getter) {
Object value;
try {
value = getter.invoke( bean );
}
catch (Exception e) {
throw new IllegalStateException( "Could not get property value", e );
}
return value;
}
public Document getDocument(T instance, Serializable id) {
Document doc = new Document();
XClass instanceClass = reflectionManager.toXClass( instance.getClass() );
Float boost = getBoost( instanceClass );
if ( boost != null ) {
doc.setBoost( boost );
}
{
Field classField =
new Field( CLASS_FIELDNAME, instanceClass.getName(), Field.Store.YES, Field.Index.UN_TOKENIZED );
doc.add( classField );
idBridge.set( idKeywordName, id, doc, Field.Store.YES, Field.Index.UN_TOKENIZED, idBoost );
}
for ( int i = 0; i < keywordNames.size(); i++ ) {
XMember member = keywordGetters.get( i );
Object value = getMemberValue( instance, member );
keywordBridges.get( i ).set(
keywordNames.get( i ), value, doc, Field.Store.YES,
Field.Index.UN_TOKENIZED, getBoost( member )
);
}
for ( int i = 0; i < textNames.size(); i++ ) {
XMember member = textGetters.get( i );
Object value = getMemberValue( instance, member );
textBridges.get( i ).set(
textNames.get( i ), value, doc, Field.Store.YES,
Field.Index.TOKENIZED, getBoost( member )
);
}
for ( int i = 0; i < unstoredNames.size(); i++ ) {
XMember member = unstoredGetters.get( i );
Object value = getMemberValue( instance, member );
unstoredBridges.get( i ).set(
unstoredNames.get( i ), value, doc, Field.Store.NO,
Field.Index.TOKENIZED, getBoost( member )
);
}
for ( int i = 0; i < fieldNames.size(); i++ ) {
XMember member = fieldGetters.get( i );
Object value = getMemberValue( instance, member );
fieldBridges.get( i ).set(
fieldNames.get( i ), value, doc, fieldStore.get( i ),
fieldIndex.get( i ), getBoost( member )
);
}
return doc;
}
public Term getTerm(Serializable id) {
return new Term( idKeywordName, idBridge.objectToString( id ) );
}
public DirectoryProvider getDirectoryProvider() {
return directoryProvider;
}
public Analyzer getAnalyzer() {
return analyzer;
}
private static void setAccessible(XMember member) {
if ( !Modifier.isPublic( member.getModifiers() ) ) {
member.setAccessible( true );
}
}
public TwoWayFieldBridge getIdBridge() {
return idBridge;
}
public String getIdKeywordName() {
return idKeywordName;
}
public static Class getDocumentClass(Document document) {
String className = document.get( DocumentBuilder.CLASS_FIELDNAME );
try {
return ReflectHelper.classForName( className );
}
catch (ClassNotFoundException e) {
throw new HibernateException( "Unable to load indexed class: " + className, e );
}
}
public static Serializable getDocumentId(FullTextIndexEventListener listener, Class clazz, Document document) {
DocumentBuilder builder = listener.getDocumentBuilders().get( clazz );
if ( builder == null ) throw new HibernateException( "No Lucene configuration set up for: " + clazz.getName() );
return (Serializable) builder.getIdBridge().get( builder.getIdKeywordName(), document );
}
public void postInitialize(Set indexedClasses) {
//this method does not requires synchronization
Class plainClass = reflectionManager.toClass( beanClass );
Set tempMappedSubclasses = new HashSet();
//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.isAssignableFrom( currentClass ) ) tempMappedSubclasses.add( currentClass );
}
mappedSubclasses = Collections.unmodifiableSet( tempMappedSubclasses );
}
public Set getMappedSubclasses() {
return mappedSubclasses;
}
}