From: <hib...@li...> - 2006-07-13 02:16:28
|
Author: epbernard Date: 2006-07-12 22:16:13 -0400 (Wed, 12 Jul 2006) New Revision: 10114 Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/FieldBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/LuceneSession.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/BridgeFactory.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/DateBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/DoubleBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FieldBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FloatBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/IntegerBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/LongBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/NumberBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/ParameterizedBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/Resolution.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/String2FieldBridgeAdaptor.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/StringBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/StringImplBridge.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/EntityInfo.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/IteratorImpl.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/LuceneQueryImpl.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/ScrollableResultsImpl.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/util/ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/util/BinderHelper.java branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/Document.java branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/MappingTest.java branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/TestCase.java branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/query/ branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/query/Book.java branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/query/Clock.java branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/query/QueryTest.java Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/event/LuceneEventListener.java Log: Prototype for Hibernate Lucene Query + field bridge Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/DocumentBuilder.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -1,7 +1,6 @@ //$Id$ package org.hibernate.lucene; -import java.beans.Introspector; import java.io.Serializable; import java.lang.reflect.AccessibleObject; import java.lang.reflect.AnnotatedElement; @@ -17,8 +16,13 @@ import org.apache.lucene.index.Term; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; +import org.hibernate.util.ReflectHelper; import org.hibernate.cfg.annotations.Version; import org.hibernate.lucene.store.DirectoryProvider; +import org.hibernate.lucene.bridge.FieldBridge; +import org.hibernate.lucene.bridge.BridgeFactory; +import org.hibernate.lucene.util.BinderHelper; +import org.hibernate.lucene.event.LuceneEventListener; //TODO handle attribute (only getters are handled currently) public class DocumentBuilder<T> { @@ -39,6 +43,8 @@ private String idKeywordName; private final Analyzer analyzer; private Float idBoost; + public static final String CLASS_FIELDNAME = "_hibernate_class"; + private FieldBridge idBridge; public DocumentBuilder(Class<?> clazz, Analyzer analyzer, DirectoryProvider directory) { //this.beanClass = clazz; @@ -51,10 +57,11 @@ Method method = methods[i]; Keyword keywordAnn = method.getAnnotation( Keyword.class ); if ( keywordAnn != null ) { - String name = getAttributeName( method, keywordAnn.name() ); + String name = BinderHelper.getAttributeName( method, keywordAnn.name() ); if ( keywordAnn.id() ) { idKeywordName = name; idBoost = getBoost( method ); + idBridge = BridgeFactory.guessType( method ); } else { setAccessible( method ); @@ -66,12 +73,12 @@ if ( unstoredAnn != null ) { setAccessible( method ); unstoredGetters.add( method ); - unstoredNames.add( getAttributeName( method, unstoredAnn.name() ) ); + unstoredNames.add( BinderHelper.getAttributeName( method, unstoredAnn.name() ) ); } Text textAnn = method.getAnnotation( Text.class ); if ( textAnn != null ) { textGetters.add( method ); - textNames.add( getAttributeName( method, textAnn.name() ) ); + textNames.add( BinderHelper.getAttributeName( method, textAnn.name() ) ); } } } @@ -111,11 +118,9 @@ doc.setBoost( boost.floatValue() ); } { - Field idField = new Field( idKeywordName, id.toString(), Field.Store.YES, Field.Index.UN_TOKENIZED ); - if (idBoost != null) { - idField.setBoost( idBoost.floatValue() ); - } - doc.add( idField ); + Field classField = new Field( CLASS_FIELDNAME, instance.getClass().getName(), Field.Store.YES, Field.Index.NO); + doc.add( classField ); + idBridge.set( idKeywordName, id, doc, Field.Store.YES, Field.Index.UN_TOKENIZED, idBoost ); } for ( int i = 0; i < keywordNames.size() ; i++ ) { Member member = keywordGetters.get( i ); @@ -161,19 +166,6 @@ return new Term( idKeywordName, id.toString() ); } - private static String getAttributeName(Method method, String name) { - if( ! "".equals( name ) ) return name; //explicit field name - - //decapitalize - String methodName = method.getName(); - //FIXME we probably should exclude methods not starting with "get" nor "is" - int startIndex = 3; - if( methodName.startsWith("is") ) { - startIndex = 2; - } - return Introspector.decapitalize( methodName.substring( startIndex ) ); - } - public DirectoryProvider getDirectoryProvider() { return directoryProvider; } @@ -187,4 +179,30 @@ ( (AccessibleObject) member ).setAccessible( true ); } } + + public FieldBridge 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(LuceneEventListener 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() ); + Serializable id = (Serializable) builder.getIdBridge().get( builder.getIdKeywordName(), document ); + return id; + } + } Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/FieldBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/FieldBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/FieldBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,22 @@ +//$Id: $ +package org.hibernate.lucene; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Documented; + +import org.hibernate.annotations.Parameter; + +/** + * specifies a given field bridge implementation + * @author Emmanuel Bernard + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Documented +public @interface FieldBridge { + public Class impl() default void.class; + public Parameter[] params() default {}; +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/LuceneSession.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/LuceneSession.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/LuceneSession.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,408 @@ +//$Id: $ +package org.hibernate.lucene; + +import java.io.Serializable; +import java.sql.Connection; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.hibernate.CacheMode; +import org.hibernate.Criteria; +import org.hibernate.EntityMode; +import org.hibernate.Filter; +import org.hibernate.FlushMode; +import org.hibernate.HibernateException; +import org.hibernate.LockMode; +import org.hibernate.Query; +import org.hibernate.ReplicationMode; +import org.hibernate.SQLQuery; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.cfg.NotYetImplementedException; +import org.hibernate.engine.query.ParameterMetadata; +import org.hibernate.impl.SessionImpl; +import org.hibernate.lucene.query.LuceneQueryImpl; +import org.hibernate.stat.SessionStatistics; +import org.hibernate.type.Type; + +/** + * Lucene aware session that allows lucene query creations + * + * @author Emmanuel Bernard + */ +public class LuceneSession implements Session { + private final SessionImpl session; + + public LuceneSession(Session session) { + this.session = (SessionImpl) session; + } + + /** + * Execute a Lucene query and retrieve managed objects + * @param entities must be immutable for the lifetime of the query object + */ + public Query createLuceneQuery(org.apache.lucene.search.Query luceneQuery, Class... entities) { + return new LuceneQueryImpl( luceneQuery, entities, session, new ParameterMetadata(null, null) ); + } + + public void index(Object object) { + throw new NotYetImplementedException(""); + //TODO + //need to add elements in a queue kept at the Session level + //the queue will be processed by a Lucene(Auto)FlushEventListener + //note that we could keep this queue somewhere in the event listener in the mean time but that requires + // a synchronized hashmap holding this queue on a per session basis plus some session house keeping (yuk) + //an other solution would be to subclass SessionImpl instead of having this LuceneSession delecation model + // this is an open discussion + } + + public Query createSQLQuery(String sql, String returnAlias, Class returnClass) { + return session.createSQLQuery( sql, returnAlias, returnClass ); + } + + public Query createSQLQuery(String sql, String[] returnAliases, Class[] returnClasses) { + return session.createSQLQuery( sql, returnAliases, returnClasses ); + } + + public int delete(String query) throws HibernateException { + return session.delete( query ); + } + + public int delete(String query, Object value, Type type) throws HibernateException { + return session.delete( query, value, type ); + } + + public int delete(String query, Object[] values, Type[] types) throws HibernateException { + return session.delete( query, values, types ); + } + + public Collection filter(Object collection, String filter) throws HibernateException { + return session.filter( collection, filter ); + } + + public Collection filter(Object collection, String filter, Object value, Type type) throws HibernateException { + return session.filter( collection, filter, value, type ); + } + + public Collection filter(Object collection, String filter, Object[] values, Type[] types) throws HibernateException { + return session.filter( collection, filter, values, types ); + } + + public List find(String query) throws HibernateException { + return session.find( query ); + } + + public List find(String query, Object value, Type type) throws HibernateException { + return session.find( query, value, type ); + } + + public List find(String query, Object[] values, Type[] types) throws HibernateException { + return session.find( query, values, types ); + } + + public Iterator iterate(String query) throws HibernateException { + return session.iterate( query ); + } + + public Iterator iterate(String query, Object value, Type type) throws HibernateException { + return session.iterate( query, value, type ); + } + + public Iterator iterate(String query, Object[] values, Type[] types) throws HibernateException { + return session.iterate( query, values, types ); + } + + public void save(String entityName, Object object, Serializable id) throws HibernateException { + session.save( entityName, object, id ); + } + + public void save(Object object, Serializable id) throws HibernateException { + session.save( object, id ); + } + + public Object saveOrUpdateCopy(String entityName, Object object) throws HibernateException { + return session.saveOrUpdateCopy( entityName, object ); + } + + public Object saveOrUpdateCopy(String entityName, Object object, Serializable id) throws HibernateException { + return session.saveOrUpdateCopy( entityName, object, id ); + } + + public Object saveOrUpdateCopy(Object object) throws HibernateException { + return session.saveOrUpdateCopy( object ); + } + + public Object saveOrUpdateCopy(Object object, Serializable id) throws HibernateException { + return session.saveOrUpdateCopy( object, id ); + } + + public void update(String entityName, Object object, Serializable id) throws HibernateException { + session.update( entityName, object, id ); + } + + public void update(Object object, Serializable id) throws HibernateException { + session.update( object, id ); + } + + public Transaction beginTransaction() throws HibernateException { + return session.beginTransaction(); + } + + public void cancelQuery() throws HibernateException { + session.cancelQuery(); + } + + public void clear() { + session.clear(); + } + + public Connection close() throws HibernateException { + return session.close(); + } + + public Connection connection() throws HibernateException { + return session.connection(); + } + + public boolean contains(Object object) { + return session.contains( object ); + } + + public Criteria createCriteria(String entityName) { + return session.createCriteria( entityName ); + } + + public Criteria createCriteria(String entityName, String alias) { + return session.createCriteria( entityName, alias ); + } + + public Criteria createCriteria(Class persistentClass) { + return session.createCriteria( persistentClass ); + } + + public Criteria createCriteria(Class persistentClass, String alias) { + return session.createCriteria( persistentClass, alias ); + } + + public Query createFilter(Object collection, String queryString) throws HibernateException { + return session.createFilter( collection, queryString ); + } + + public Query createQuery(String queryString) throws HibernateException { + return session.createQuery( queryString ); + } + + public SQLQuery createSQLQuery(String queryString) throws HibernateException { + return session.createSQLQuery( queryString ); + } + + public void delete(String entityName, Object object) throws HibernateException { + session.delete( entityName, object ); + } + + public void delete(Object object) throws HibernateException { + session.delete( object ); + } + + public void disableFilter(String filterName) { + session.disableFilter( filterName ); + } + + public Connection disconnect() throws HibernateException { + return session.disconnect(); + } + + public Filter enableFilter(String filterName) { + return session.enableFilter( filterName ); + } + + public void evict(Object object) throws HibernateException { + session.evict( object ); + } + + public void flush() throws HibernateException { + session.flush(); + } + + public Object get(Class clazz, Serializable id) throws HibernateException { + return session.get( clazz, id ); + } + + public Object get(Class clazz, Serializable id, LockMode lockMode) throws HibernateException { + return session.get( clazz, id, lockMode ); + } + + public Object get(String entityName, Serializable id) throws HibernateException { + return session.get( entityName, id ); + } + + public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException { + return session.get( entityName, id, lockMode ); + } + + public CacheMode getCacheMode() { + return session.getCacheMode(); + } + + public LockMode getCurrentLockMode(Object object) throws HibernateException { + return session.getCurrentLockMode( object ); + } + + public Filter getEnabledFilter(String filterName) { + return session.getEnabledFilter( filterName ); + } + + public EntityMode getEntityMode() { + return session.getEntityMode(); + } + + public String getEntityName(Object object) throws HibernateException { + return session.getEntityName( object ); + } + + public FlushMode getFlushMode() { + return session.getFlushMode(); + } + + public Serializable getIdentifier(Object object) throws HibernateException { + return session.getIdentifier( object ); + } + + public Query getNamedQuery(String queryName) throws HibernateException { + return session.getNamedQuery( queryName ); + } + + public org.hibernate.Session getSession(EntityMode entityMode) { + return session.getSession( entityMode ); + } + + public SessionFactory getSessionFactory() { + return session.getSessionFactory(); + } + + public SessionStatistics getStatistics() { + return session.getStatistics(); + } + + public Transaction getTransaction() { + return session.getTransaction(); + } + + public boolean isConnected() { + return session.isConnected(); + } + + public boolean isDirty() throws HibernateException { + return session.isDirty(); + } + + public boolean isOpen() { + return session.isOpen(); + } + + public Object load(String entityName, Serializable id) throws HibernateException { + return session.load( entityName, id ); + } + + public Object load(String entityName, Serializable id, LockMode lockMode) throws HibernateException { + return session.load( entityName, id, lockMode ); + } + + public void load(Object object, Serializable id) throws HibernateException { + session.load( object, id ); + } + + public Object load(Class theClass, Serializable id) throws HibernateException { + return session.load( theClass, id ); + } + + public Object load(Class theClass, Serializable id, LockMode lockMode) throws HibernateException { + return session.load( theClass, id, lockMode ); + } + + public void lock(String entityName, Object object, LockMode lockMode) throws HibernateException { + session.lock( entityName, object, lockMode ); + } + + public void lock(Object object, LockMode lockMode) throws HibernateException { + session.lock( object, lockMode ); + } + + public Object merge(String entityName, Object object) throws HibernateException { + return session.merge( entityName, object ); + } + + public Object merge(Object object) throws HibernateException { + return session.merge( object ); + } + + public void persist(String entityName, Object object) throws HibernateException { + session.persist( entityName, object ); + } + + public void persist(Object object) throws HibernateException { + session.persist( object ); + } + + public void reconnect() throws HibernateException { + session.reconnect(); + } + + public void reconnect(Connection connection) throws HibernateException { + session.reconnect( connection ); + } + + public void refresh(Object object) throws HibernateException { + session.refresh( object ); + } + + public void refresh(Object object, LockMode lockMode) throws HibernateException { + session.refresh( object, lockMode ); + } + + public void replicate(String entityName, Object object, ReplicationMode replicationMode) throws HibernateException { + session.replicate( entityName, object, replicationMode ); + } + + public void replicate(Object object, ReplicationMode replicationMode) throws HibernateException { + session.replicate( object, replicationMode ); + } + + public Serializable save(String entityName, Object object) throws HibernateException { + return session.save( entityName, object ); + } + + public Serializable save(Object object) throws HibernateException { + return session.save( object ); + } + + public void saveOrUpdate(String entityName, Object object) throws HibernateException { + session.saveOrUpdate( entityName, object ); + } + + public void saveOrUpdate(Object object) throws HibernateException { + session.saveOrUpdate( object ); + } + + public void setCacheMode(CacheMode cacheMode) { + session.setCacheMode( cacheMode ); + } + + public void setFlushMode(FlushMode flushMode) { + session.setFlushMode( flushMode ); + } + + public void setReadOnly(Object entity, boolean readOnly) { + session.setReadOnly( entity, readOnly ); + } + + public void update(String entityName, Object object) throws HibernateException { + session.update( entityName, object ); + } + + public void update(Object object) throws HibernateException { + session.update( object ); + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/BridgeFactory.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/BridgeFactory.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/BridgeFactory.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,99 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Member; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.hibernate.HibernateException; +import org.hibernate.annotations.Parameter; +import org.hibernate.lucene.util.BinderHelper; + +/** + * @author Emmanuel Bernard + */ +public class BridgeFactory { + private static Map<String, FieldBridge> builtInBridges = new HashMap<String, FieldBridge>(); + private BridgeFactory() {} + + public static final FieldBridge DOUBLE = new String2FieldBridgeAdaptor( new DoubleBridge() ); + + public static final FieldBridge FLOAT = new String2FieldBridgeAdaptor( new FloatBridge() ); + + public static final FieldBridge INTEGER = new String2FieldBridgeAdaptor( new IntegerBridge() ); + + public static final FieldBridge LONG = new String2FieldBridgeAdaptor( new LongBridge() ); + + public static final FieldBridge STRING = new String2FieldBridgeAdaptor( new StringImplBridge() ); + + public static final FieldBridge DATE_YEAR; + public static final FieldBridge DATE_MONTH; + public static final FieldBridge DATE_DAY; + public static final FieldBridge DATE_HOUR; + public static final FieldBridge DATE_MINUTE; + public static final FieldBridge DATE_SECOND; + public static final FieldBridge DATE_MILLISECOND; + + static { + builtInBridges.put( Double.class.getName(), DOUBLE ); + builtInBridges.put( Float.class.getName(), FLOAT ); + builtInBridges.put( Integer.class.getName(), INTEGER ); + builtInBridges.put( Long.class.getName(), LONG ); + builtInBridges.put( String.class.getName(), STRING ); + + DATE_YEAR = createDateBridge( Resolution.YEAR ); + DATE_MONTH = createDateBridge( Resolution.MONTH ); + DATE_DAY = createDateBridge( Resolution.DAY ); + DATE_HOUR = createDateBridge( Resolution.HOUR ); + DATE_MINUTE = createDateBridge( Resolution.MINUTE ); + DATE_SECOND = createDateBridge( Resolution.SECOND ); + DATE_MILLISECOND = createDateBridge( Resolution.MILLISECOND ); + builtInBridges.put( Date.class.getName(), DATE_MILLISECOND ); + } + + private static FieldBridge createDateBridge(Resolution resolution) { + DateBridge date; + Map params = new HashMap(1); + params.put( "resolution", resolution ); + date = new DateBridge(); + date.setParameterValues( params ); + return new String2FieldBridgeAdaptor( date ); + } + + public static FieldBridge guessType(Member member) { + FieldBridge bridge = null; + org.hibernate.lucene.FieldBridge bridgeAnn = ( (AnnotatedElement) member ).getAnnotation( org.hibernate.lucene.FieldBridge.class ); + if (bridgeAnn != null) { + Class impl = bridgeAnn.impl(); + try { + Object instance = impl.newInstance(); + if ( FieldBridge.class.isAssignableFrom( impl ) ) { + bridge = (FieldBridge) instance; + } + else if ( StringBridge.class.isAssignableFrom( impl ) ) { + bridge = new String2FieldBridgeAdaptor( (StringBridge) instance ); + } + if ( bridgeAnn.params().length > 0 && ParameterizedBridge.class.isAssignableFrom( impl ) ) { + Map params = new HashMap( bridgeAnn.params().length ); + for ( Parameter param : bridgeAnn.params() ) { + params.put( param.name(), param.value() ); + } + ( (ParameterizedBridge) instance ).setParameterValues( params ); + } + } + catch (Exception e) { + throw new HibernateException("Unable to instanciate FieldBridge for " + BinderHelper.getAttributeName(member), e ); + } + } + else { + //find in built-ins + Class<?> returnType = BinderHelper.getReturnType( member ); + bridge = builtInBridges.get( returnType.getName() ); + } + if (bridge == null) throw new HibernateException("Unable to guess FieldBridge for " + BinderHelper.getAttributeName(member) ); + return bridge; + } + +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/DateBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/DateBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/DateBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,62 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +import java.text.ParseException; +import java.util.Date; +import java.util.Map; + +import org.apache.lucene.document.DateTools; +import org.hibernate.AssertionFailure; +import org.hibernate.HibernateException; + +/** + * @author Emmanuel Bernard + */ +public class DateBridge implements StringBridge, ParameterizedBridge { + //TODO don't depend on such a weak 3rd party API for a public API of ours + DateTools.Resolution resolution; + + public Object stringToObject(String stringValue) { + //usually does not make sense + try { + return DateTools.stringToDate( stringValue ); + } + catch (ParseException e) { + throw new HibernateException( "Unable to parse into date: " + stringValue, e ); + } + } + + public String objectToString(Object object) { + return DateTools.dateToString( (Date) object, resolution ); + } + + public void setParameterValues(Map parameters) { + Resolution hibResolution = (Resolution) parameters.get( "resolution" ); + switch ( hibResolution ) { + case YEAR: + resolution = DateTools.Resolution.YEAR; + break; + case MONTH: + resolution = DateTools.Resolution.MONTH; + break; + case DAY: + resolution = DateTools.Resolution.DAY; + break; + case HOUR: + resolution = DateTools.Resolution.HOUR; + break; + case MINUTE: + resolution = DateTools.Resolution.MINUTE; + break; + case SECOND: + resolution = DateTools.Resolution.SECOND; + break; + case MILLISECOND: + resolution = DateTools.Resolution.MILLISECOND; + break; + default: + throw new AssertionFailure( "Unknown Resolution: " + hibResolution ); + + } + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/DoubleBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/DoubleBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/DoubleBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,11 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +/** + * @author Emmanuel Bernard + */ +public class DoubleBridge extends NumberBridge { + public Object stringToObject(String stringValue) { + return new Double(stringValue); + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FieldBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FieldBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FieldBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,18 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; + +/** + * Link between a java property and a Lucene Document + * Usually a Java property will be linked to a Document Field + * + * @author Emmanuel Bernard + */ +//TODO should show Field or document? +//document is nice since I can save an object into several fields +public interface FieldBridge { + Object get(String name, Document document); + void set(String name, Object value, Document document, Field.Store store, Field.Index index, Float boost); +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FloatBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FloatBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/FloatBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,11 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +/** + * @author Emmanuel Bernard + */ +public class FloatBridge extends NumberBridge { + public Object stringToObject(String stringValue) { + return new Float(stringValue); + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/IntegerBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/IntegerBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/IntegerBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,11 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +/** + * @author Emmanuel Bernard + */ +public class IntegerBridge extends NumberBridge { + public Object stringToObject(String stringValue) { + return new Integer(stringValue); + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/LongBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/LongBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/LongBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,11 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +/** + * @author Emmanuel Bernard + */ +public class LongBridge extends NumberBridge { + public Object stringToObject(String stringValue) { + return new Long(stringValue); + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/NumberBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/NumberBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/NumberBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,11 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +/** + * @author Emmanuel Bernard + */ +public abstract class NumberBridge implements StringBridge { + public String objectToString(Object object) { + return object.toString(); + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/ParameterizedBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/ParameterizedBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/ParameterizedBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,13 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +import java.util.Map; + +/** + * Allow parameter injection to a given bridge + * + * @author Emmanuel Bernard + */ +public interface ParameterizedBridge { + void setParameterValues(Map parameters); +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/Resolution.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/Resolution.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/Resolution.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,17 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +/** + * Date indexing resolution + * + * @author Emmanuel Bernard + */ +public enum Resolution { + YEAR, + MONTH, + DAY, + HOUR, + MINUTE, + SECOND, + MILLISECOND +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/String2FieldBridgeAdaptor.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/String2FieldBridgeAdaptor.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/String2FieldBridgeAdaptor.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,31 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; + +/** + * Bridge to use a Java2String as a Java2Field + * + * @author Emmanuel Bernard + */ +public class String2FieldBridgeAdaptor implements FieldBridge { + + private StringBridge stringBridge; + + public String2FieldBridgeAdaptor(StringBridge stringBridge) { + this.stringBridge = stringBridge; + } + public Object get(String name, Document document) { + Field field = document.getField( name ); + return stringBridge.stringToObject( field.stringValue() ); + } + + public void set(String name, Object value, Document document, Field.Store store, Field.Index index, Float boost) { + Field field = new Field(name, stringBridge.objectToString( value ), store, index); + if (boost != null) field.setBoost( boost ); + document.add( field ); + } + + +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/StringBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/StringBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/StringBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,20 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +/** + * Transform an object into a stirng representation and vice versa + * + * @author Emmanuel Bernard + */ +public interface StringBridge { + /** + * Convert the string representation to an object + * FIXME: This operation might not always be possible + */ + Object stringToObject(String stringValue); + + /** + * convert the object representation to a String + */ + String objectToString(Object object); +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/StringImplBridge.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/StringImplBridge.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/bridge/StringImplBridge.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,15 @@ +//$Id: $ +package org.hibernate.lucene.bridge; + +/** + * @author Emmanuel Bernard + */ +public class StringImplBridge implements StringBridge { + public Object stringToObject(String stringValue) { + return stringValue; + } + + public String objectToString(Object object) { + return (String) object; + } +} Modified: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/event/LuceneEventListener.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/event/LuceneEventListener.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/event/LuceneEventListener.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -41,10 +41,17 @@ * @author Emmanuel Bernard * @author Mattias Arbin */ -//TODO takes care of synchronization and index concurrent index change +//TODO work on sharing the same indexWriters and readers across a single post operation... public class LuceneEventListener implements PostDeleteEventListener, PostInsertEventListener, PostUpdateEventListener, Initializable { + //FIXME keeping this here is a bad decision since you might want to search indexes wo maintain it + @Deprecated + public Map<Class, DocumentBuilder<Object>> getDocumentBuilders() { + return documentBuilders; + } + + private Map<Class, DocumentBuilder<Object>> documentBuilders = new HashMap<Class, DocumentBuilder<Object>>(); //** keep track of the index modifiers per file since 1 index modifier can be present at a time */ private Map<DirectoryProvider, Lock> indexLock = new HashMap<DirectoryProvider, Lock>(); Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/EntityInfo.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/EntityInfo.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/EntityInfo.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,12 @@ +//$Id: $ +package org.hibernate.lucene.query; + +import java.io.Serializable; + +/** + * @author Emmanuel Bernard + */ +class EntityInfo { + public Class clazz; + public Serializable id; +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/IteratorImpl.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/IteratorImpl.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/IteratorImpl.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,38 @@ +//$Id: $ +package org.hibernate.lucene.query; + +import java.util.Iterator; + +import org.hibernate.Session; + +/** + * @author Emmanuel Bernard + */ +public class IteratorImpl implements Iterator { + + private final EntityInfo[] entityInfos; + private final Session session; + private int index = 0; + private final int size; + + public IteratorImpl(EntityInfo[] entityInfos, Session session) { + this.entityInfos = entityInfos; + this.session = session; + this.size = entityInfos.length; + } + + public boolean hasNext() { + return index < size; + } + + public Object next() { + Object object = session.get( entityInfos[index].clazz, entityInfos[index].id ); + index++; + return object; + } + + public void remove() { + //TODO this is theorically doable + throw new UnsupportedOperationException( "Cannot remove from a lucene query interator" ); + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/LuceneQueryImpl.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/LuceneQueryImpl.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/LuceneQueryImpl.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,258 @@ +//$Id: $ +package org.hibernate.lucene.query; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.document.Document; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MultiSearcher; +import org.apache.lucene.search.Searcher; +import org.apache.lucene.store.Directory; +import org.hibernate.Hibernate; +import org.hibernate.HibernateException; +import org.hibernate.LockMode; +import org.hibernate.Query; +import org.hibernate.ScrollMode; +import org.hibernate.ScrollableResults; +import org.hibernate.Session; +import org.hibernate.engine.SessionImplementor; +import org.hibernate.engine.query.ParameterMetadata; +import org.hibernate.event.PostInsertEventListener; +import org.hibernate.impl.AbstractQueryImpl; +import org.hibernate.lucene.DocumentBuilder; +import org.hibernate.lucene.event.LuceneEventListener; + +/** + * @author Emmanuel Bernard + */ +//implements setParameter() +public class LuceneQueryImpl extends AbstractQueryImpl { + private static final Log log = LogFactory.getLog( LuceneQueryImpl.class ); + private org.apache.lucene.search.Query luceneQuery; + private Class[] classes; + private Integer firstResult; + private Integer maxResults; + private int resultSize; + + /** classes must be immutable */ + public LuceneQueryImpl(org.apache.lucene.search.Query query, Class[] classes, SessionImplementor session, ParameterMetadata parameterMetadata) { + //TODO handle flushMode + super( query.toString(), null, session, parameterMetadata ); + this.luceneQuery = query; + this.classes = classes; + } + + /** + * Return an interator on the results. + * Retrieve the object one by one (initialize it during the next() operation) + */ + public Iterator iterate() throws HibernateException { + //implement an interator which keep the id/class for each hit and get the object on demand + //cause I can't keep the searcher and hence the hit opened. I dont have any hook to know when the + //user stop using it + //scrollable is better in this area + + LuceneEventListener listener = getLuceneEventListener(); + //find the directories + Searcher searcher = buildSearcher( listener ); + Hits hits; + try { + hits = searcher.search( luceneQuery ); + setResultSize(hits); + int first = first(); + int max = max( first, hits ); + EntityInfo[] entityInfos = new EntityInfo[max - first + 1]; + for (int index = first ; index <= max ; index++ ) { + Document document = hits.doc( index ); + EntityInfo entityInfo = new EntityInfo(); + entityInfo.clazz = DocumentBuilder.getDocumentClass( document ); + //FIXME should check that clazz match classes but this complexify a lot the firstResult/maxResult + entityInfo.id = DocumentBuilder.getDocumentId( listener, entityInfo.clazz, document ); + entityInfos[ index - first ] = entityInfo; + } + return new IteratorImpl( entityInfos, (Session) this.session ); + } + catch (IOException e) { + throw new HibernateException("Unable to query Lucene index", e); + } + finally { + if (searcher != null) try { + searcher.close(); + } + catch (IOException e) { + log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e ); + } + } + } + + public ScrollableResults scroll() throws HibernateException { + //keep the searcher open until the resultset is closed + LuceneEventListener listener = getLuceneEventListener(); + //find the directories + Searcher searcher = buildSearcher( listener ); + Hits hits; + try { + hits = searcher.search( luceneQuery ); + setResultSize(hits); + int first = first(); + int max = max( first, hits ); + return new ScrollableResultsImpl( searcher, hits, first, max, (Session) this.session, listener ); + } + catch (IOException e) { + try { + if ( searcher != null ) searcher.close(); + } + catch (IOException ee) { + //we have the initial issue already + } + throw new HibernateException("Unable to query Lucene index", e); + } + } + + public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException { + //TODO think about this scrollmode + return scroll(); + } + + public List list() throws HibernateException { + LuceneEventListener listener = getLuceneEventListener(); + //find the directories + Searcher searcher = buildSearcher( listener ); + Hits hits; + try { + hits = searcher.search( luceneQuery ); + setResultSize(hits); + int first = first(); + int max = max( first, hits ); + List result = new ArrayList( max - first + 1); + Session sess = (Session) this.session; + for (int index = first ; index <= max ; index++ ) { + Document document = hits.doc( index ); + Class clazz = DocumentBuilder.getDocumentClass( document ); + //FIXME should check that clazz match classes but this complexify a lot the firstResult/maxResult + Serializable id = DocumentBuilder.getDocumentId( listener, clazz, document ); + result.add( sess.load( clazz, id ) ); + //use load to benefit from the batch-size (but facing some proxy casting issues... + } + //then initialize the objects + for (Object element : result) { + Hibernate.initialize(element); + } + return result; + } + catch (IOException e) { + throw new HibernateException("Unable to query Lucene index", e); + } + finally { + if (searcher != null) try { + searcher.close(); + } + catch (IOException e) { + log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e ); + } + } + } + + private int max(int first, Hits hits) { + return maxResults == null ? + first + hits.length() - 1 : + maxResults + first < hits.length() ? + first + maxResults : + hits.length() - 1; + } + + private int first() { + return firstResult != null ? firstResult : 0; + } + + private Searcher buildSearcher(LuceneEventListener listener) { + Map<Class, DocumentBuilder<Object>> builders = listener.getDocumentBuilders(); + Set<Directory> directories = new HashSet<Directory>(); + for (Class clazz : classes) { + DocumentBuilder builder = builders.get(clazz); + if (builder == null) throw new HibernateException( "Not a mapped entity: " + clazz); + directories.add( builder.getDirectoryProvider().getDirectory() ); + } + + //set up the searcher + Searcher searcher; + int dirNbr = directories.size(); + if (dirNbr > 1) { + try { + IndexSearcher[] searchers = new IndexSearcher[ dirNbr ]; + Iterator<Directory> it = directories.iterator(); + for (int index = 0 ; index < dirNbr ; index++) { + searchers[index] = new IndexSearcher( it.next() ); + } + searcher = new MultiSearcher(searchers); + } + catch(IOException e) { + throw new HibernateException("Unable to read Lucene directory", e); + } + } + else { + try { + searcher = new IndexSearcher( directories.iterator().next() ); + } + catch (IOException e) { + throw new HibernateException("Unable to read Lucene directory", e); + } + } + return searcher; + } + + private void setResultSize(Hits hits) { + resultSize = hits.length(); + } + + //FIXME does it make sense + public int resultSize() { + return this.resultSize; + } + + public Query setFirstResult(int firstResult) { + this.firstResult = firstResult; + return this; + } + + public Query setMaxResults(int maxResults) { + this.maxResults = maxResults; + return this; + } + + private LuceneEventListener getLuceneEventListener() { + PostInsertEventListener[] listeners = session.getListeners().getPostCommitInsertEventListeners(); + LuceneEventListener listener = null; + //FIXME this sucks since we mandante the event listener use + for(PostInsertEventListener candidate : listeners ) { + if (candidate instanceof LuceneEventListener) { + listener = (LuceneEventListener) candidate; + break; + } + } + if (listener == null) throw new HibernateException("Lucene event listener not initialized"); + return listener; + } + + public int executeUpdate() throws HibernateException { + throw new HibernateException( "Not supported operation" ); + } + + public Query setLockMode(String alias, LockMode lockMode) { + return null; + } + + protected Map getLockModes() { + return null; + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/ScrollableResultsImpl.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/ScrollableResultsImpl.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/query/ScrollableResultsImpl.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,217 @@ +//$Id: $ +package org.hibernate.lucene.query; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Blob; +import java.sql.Clob; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import org.apache.lucene.document.Document; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.Searcher; +import org.hibernate.HibernateException; +import org.hibernate.ScrollableResults; +import org.hibernate.Session; +import org.hibernate.lucene.DocumentBuilder; +import org.hibernate.lucene.event.LuceneEventListener; +import org.hibernate.type.Type; + +/** + * @author Emmanuel Bernard + */ +public class ScrollableResultsImpl implements ScrollableResults { + private final Searcher searcher; + private final Hits hits; + private final int first; + private final int max; + private int current; + private final Session session; + private final LuceneEventListener listener; + private EntityInfo[] entityInfos; + + public ScrollableResultsImpl( + Searcher searcher, Hits hits, int first, int max, Session session, LuceneEventListener listener + ) { + this.searcher = searcher; + this.hits = hits; + this.first = first; + this.max = max; + this.current = first; + this.session = session; + this.listener = listener; + entityInfos = new EntityInfo[max - first + 1]; + } + public boolean next() throws HibernateException { + return ++current <= max; + } + + public boolean previous() throws HibernateException { + return --current >= first; + } + + public boolean scroll(int i) throws HibernateException { + current = current + i; + return current >= first && current <= max; + } + + public boolean last() throws HibernateException { + current = max; + return max >= first; + } + + public boolean first() throws HibernateException { + current = first; + return max >= first; + } + + public void beforeFirst() throws HibernateException { + current = first - 1; + } + + public void afterLast() throws HibernateException { + current = max + 1; + } + + public boolean isFirst() throws HibernateException { + return current == first; + } + + public boolean isLast() throws HibernateException { + return current == max; + } + + public void close() throws HibernateException { + try { + searcher.close(); + } + catch (IOException e) { + throw new HibernateException( "Unable to close Lucene searcher", e); + } + } + + public Object[] get() throws HibernateException { + if (current < first || current > max) return null; //or exception? + EntityInfo info = entityInfos[current - first]; + if ( info == null ) { + info = new EntityInfo(); + Document document = null; + try { + document = hits.doc( current ); + } + catch (IOException e) { + throw new HibernateException( "Unable to read Lucene hits[" + current + "]", e); + } + info.clazz = DocumentBuilder.getDocumentClass( document ); + //FIXME should check that clazz match classes but this complexify a lot the firstResult/maxResult + info.id = DocumentBuilder.getDocumentId( listener, info.clazz, document ); + entityInfos[current - first] = info; + } + return new Object[] { + session.get( info.clazz, info.id ) + }; + } + + public Object get(int i) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Type getType(int i) { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Integer getInteger(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Long getLong(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Float getFloat(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Boolean getBoolean(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Double getDouble(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Short getShort(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Byte getByte(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Character getCharacter(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public byte[] getBinary(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public String getText(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Blob getBlob(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Clob getClob(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public String getString(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public BigDecimal getBigDecimal(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public BigInteger getBigInteger(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Date getDate(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Locale getLocale(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public Calendar getCalendar(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public TimeZone getTimeZone(int col) throws HibernateException { + throw new UnsupportedOperationException( "Lucene does not work on columns" ); + } + + public int getRowNumber() throws HibernateException { + if ( max < first ) return -1; + return current - first; + } + + public boolean setRowNumber(int rowNumber) throws HibernateException { + if (rowNumber >= 0) { + current = first + rowNumber; + } + else { + current = max + rowNumber + 1; //max row start at -1 + } + return current >= first && current <= max; + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/util/BinderHelper.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/util/BinderHelper.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/java/org/hibernate/lucene/util/BinderHelper.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,51 @@ +//$Id: $ +package org.hibernate.lucene.util; + +import java.lang.reflect.Method; +import java.lang.reflect.Member; +import java.lang.reflect.Field; +import java.beans.Introspector; + +import org.hibernate.util.StringHelper; + +/** + * @author Emmanuel Bernard + */ +public abstract class BinderHelper { + + private BinderHelper() {} + + public static String getAttributeName(Member member) { + return getAttributeName( member, null ); + } + /** + * Get attribute name out of member unless overriden by <code>name</code> + */ + //TODO move to reflection layer + public static String getAttributeName(Member member, String name) { + if( StringHelper.isNotEmpty( name ) ) return name; //explicit field name + if (member instanceof Field ) { + return ( (Field) member ).getName(); + } + else { + //decapitalize + String methodName = ( (Method) member).getName(); + //FIXME we probably should exclude methods not starting with "get" nor "is" + int startIndex = 3; + if( methodName.startsWith("is") ) { + startIndex = 2; + } + return Introspector.decapitalize( methodName.substring( startIndex ) ); + } + } + + //TODO move to reflection layer + public static Class<?> getReturnType(Member member) { + if (member instanceof Field) { + return ( (Field) member ).getType(); + } + else { + return ( (Method) member ).getReturnType(); + } + } +} Added: branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/Document.java =================================================================== --- branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/Document.java 2006-07-13 01:52:08 UTC (rev 10113) +++ branches/Lucene_Integration/HibernateExt/metadata/src/test/org/hibernate/lucene/test/Document.java 2006-07-13 02:16:13 UTC (rev 10114) @@ -0,0 +1,72 @@ +//$Id: Document.java 10014 2006-06-12 09:56:27 -0700 (lun., 12 juin 2006) epbernard $ +package org.hibernate.lucene.test; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Lob; + +import org.hibernate.lu... [truncated message content] |