[hibernate-commits] Hibernate SVN: r14524 - in search/trunk: src/java/org/hibernate/search/annotations and 5 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue Apr 22 22:31:14 EDT 2008


Author: epbernard
Date: 2008-04-22 22:31:14 -0400 (Tue, 22 Apr 2008)
New Revision: 14524

Added:
   search/trunk/lib/apache-solr-1.2.0.jar
   search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDef.java
   search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDefs.java
   search/trunk/src/java/org/hibernate/search/annotations/TokenFilterDef.java
   search/trunk/src/java/org/hibernate/search/annotations/TokenizerDef.java
   search/trunk/src/java/org/hibernate/search/impl/InitContext.java
   search/trunk/src/java/org/hibernate/search/util/DelegateNamedAnalyzer.java
   search/trunk/src/test/org/hibernate/search/test/analyzer/solr/
   search/trunk/src/test/org/hibernate/search/test/analyzer/solr/SolrAnalyzerTest.java
   search/trunk/src/test/org/hibernate/search/test/analyzer/solr/Team.java
   search/trunk/src/test/org/hibernate/search/test/analyzer/solr/stoplist.properties
Modified:
   search/trunk/lib/README.txt
   search/trunk/src/java/org/hibernate/search/annotations/Analyzer.java
   search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
   search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
Log:
HSEARCH-186 Integrate Solr analyzer infrastructure

Modified: search/trunk/lib/README.txt
===================================================================
--- search/trunk/lib/README.txt	2008-04-22 15:04:59 UTC (rev 14523)
+++ search/trunk/lib/README.txt	2008-04-23 02:31:14 UTC (rev 14524)
@@ -8,6 +8,7 @@
 hibernate core dependencies: required (see Hibernate Core for more information)
 lucene-core-*.jar: required (used version 2.3.0)
 jms.jar: optional (needed for JMS based clustering strategy, usually available with your application server)
+apache-solr-*.jar: optional (used version 1.2.0), needed if @AnalyzerDef is used
 
 Test
 ====

Added: search/trunk/lib/apache-solr-1.2.0.jar
===================================================================
(Binary files differ)


Property changes on: search/trunk/lib/apache-solr-1.2.0.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Modified: search/trunk/src/java/org/hibernate/search/annotations/Analyzer.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/Analyzer.java	2008-04-22 15:04:59 UTC (rev 14523)
+++ search/trunk/src/java/org/hibernate/search/annotations/Analyzer.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -8,13 +8,16 @@
 import java.lang.annotation.Documented;
 
 /**
- * Define an Analizer for a given entity, method, field or Field
+ * Define an Analyzer for a given entity, method, field or Field
  * The order of precedence is as such:
  *  - @Field
  *  - field / method
  *  - entity
  *  - default
  *
+ * Either describe an explicit implementation through the <code>impl</code> parameter
+ * or use an external @AnalyzerDef definition through the <code>def</code> parameter
+ *
  * @author Emmanuel Bernard
  */
 @Retention( RetentionPolicy.RUNTIME )
@@ -23,4 +26,5 @@
 
 public @interface Analyzer {
 	Class impl() default void.class;
+	String definition() default "";
 }

Added: search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDef.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDef.java	                        (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDef.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,39 @@
+//$
+package org.hibernate.search.annotations;
+
+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;
+
+/**
+ * Reusable analyzer definition.
+ * An analyzer definition defines:
+ *  - one tokenizer
+ *  - optionally some filters
+ * Filters are applied in the order they are defined
+ *
+ * Reuses the Solr Tokenizer and Filter architecture
+ *
+ * @author Emmanuel Bernard
+ */
+ at Retention( RetentionPolicy.RUNTIME )
+ at Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD} )
+ at Documented
+public @interface AnalyzerDef {
+	/**
+	 * Reference name to be used on {#org.hibernate.search.annotations.Analyzer}
+	 */
+	String name();
+
+	/**
+	 * Tokenizer used
+	 */
+	TokenizerDef tokenizer();
+
+	/**
+	 * Filters used. The filters are applied in the defined order
+	 */
+	TokenFilterDef[] filters() default {};
+}

Added: search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDefs.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDefs.java	                        (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/AnalyzerDefs.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,20 @@
+package org.hibernate.search.annotations;
+
+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;
+
+/**
+ * Reusable analyzer definitions.
+ * This annotation allows multiple definition declarations per element
+ *
+ * @author Emmanuel Bernard
+ */
+ at Retention( RetentionPolicy.RUNTIME )
+ at Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD} )
+ at Documented
+public @interface AnalyzerDefs {
+	AnalyzerDef[] value();
+}

Added: search/trunk/src/java/org/hibernate/search/annotations/TokenFilterDef.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/TokenFilterDef.java	                        (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/TokenFilterDef.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,30 @@
+//$
+package org.hibernate.search.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.solr.analysis.TokenFilterFactory;
+
+/**
+ * Define a TokenFilterFactory and its parameters
+ *
+ * @author Emmanuel Bernard
+ */
+ at Retention( RetentionPolicy.RUNTIME )
+ at Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD } )
+ at Documented
+public @interface TokenFilterDef {
+	/**
+	 * Defines the TokenFilterFactory implementation used
+	 */
+	public abstract Class<? extends TokenFilterFactory> factory();
+
+	/**
+	 * optional parameters passed to the TokenFilterFactory
+	 */
+	public abstract Parameter[] params() default {};
+}
\ No newline at end of file

Added: search/trunk/src/java/org/hibernate/search/annotations/TokenizerDef.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/TokenizerDef.java	                        (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/TokenizerDef.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,30 @@
+//$
+package org.hibernate.search.annotations;
+
+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.apache.solr.analysis.TokenizerFactory;
+
+/**
+ * Define a TokenizerFactory and its parameters
+ * 
+ * @author Emmanuel Bernard
+ */
+ at Retention( RetentionPolicy.RUNTIME )
+ at Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD} )
+ at Documented
+public @interface TokenizerDef {
+	/**
+	 * Defines the TokenizerFactory implementation used
+	 */
+	Class<? extends TokenizerFactory> factory();
+
+	/**
+	 * optional parameters passed to the TokenizerFactory
+	 */
+	Parameter[] params() default {};
+}

Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java	2008-04-22 15:04:59 UTC (rev 14523)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -26,8 +26,10 @@
 import org.hibernate.annotations.common.reflection.XMember;
 import org.hibernate.annotations.common.reflection.XProperty;
 import org.hibernate.annotations.common.util.ReflectHelper;
+import org.hibernate.annotations.common.util.StringHelper;
 import org.hibernate.proxy.HibernateProxy;
 import org.hibernate.search.SearchException;
+import org.hibernate.search.impl.InitContext;
 import org.hibernate.search.annotations.Boost;
 import org.hibernate.search.annotations.ClassBridge;
 import org.hibernate.search.annotations.ClassBridges;
@@ -37,6 +39,8 @@
 import org.hibernate.search.annotations.IndexedEmbedded;
 import org.hibernate.search.annotations.Store;
 import org.hibernate.search.annotations.TermVector;
+import org.hibernate.search.annotations.AnalyzerDefs;
+import org.hibernate.search.annotations.AnalyzerDef;
 import org.hibernate.search.backend.AddLuceneWork;
 import org.hibernate.search.backend.DeleteLuceneWork;
 import org.hibernate.search.backend.LuceneWork;
@@ -79,7 +83,7 @@
 	private Similarity similarity;
 
 
-	public DocumentBuilder(XClass clazz, Analyzer defaultAnalyzer, Similarity defaultSimilarity, DirectoryProvider[] directoryProviders,
+	public DocumentBuilder(XClass clazz, InitContext context, DirectoryProvider[] directoryProviders,
 						   IndexShardingStrategy shardingStrategy, ReflectionManager reflectionManager) {
 		this.analyzer = new ScopedAnalyzer();
 		this.beanClass = clazz;
@@ -87,15 +91,15 @@
 		this.shardingStrategy = shardingStrategy;
 		//FIXME get rid of it when boost is stored?
 		this.reflectionManager = reflectionManager;
-		this.similarity = defaultSimilarity;
+		this.similarity = context.getDefaultSimilarity();
 
 		if ( clazz == null ) throw new AssertionFailure( "Unable to build a DocumentBuilder with a null class" );
 		rootPropertiesMetadata = new PropertiesMetadata();
 		rootPropertiesMetadata.boost = getBoost( clazz );
-		rootPropertiesMetadata.analyzer = defaultAnalyzer;
+		rootPropertiesMetadata.analyzer = context.getDefaultAnalyzer();
 		Set<XClass> processedClasses = new HashSet<XClass>();
 		processedClasses.add( clazz );
-		initializeMembers( clazz, rootPropertiesMetadata, true, "", processedClasses );
+		initializeMembers( clazz, rootPropertiesMetadata, true, "", processedClasses, context );
 		//processedClasses.remove( clazz ); for the sake of completness
 		this.analyzer.setGlobalAnalyzer( rootPropertiesMetadata.analyzer );
 		if ( idKeywordName == null ) {
@@ -103,16 +107,23 @@
 		}
 	}
 
-	private Analyzer getAnalyzer(XAnnotatedElement annotatedElement) {
+	private Analyzer getAnalyzer(XAnnotatedElement annotatedElement, InitContext context) {
 		org.hibernate.search.annotations.Analyzer analyzerAnn =
 				annotatedElement.getAnnotation( org.hibernate.search.annotations.Analyzer.class );
-		return getAnalyzer( analyzerAnn );
+		return getAnalyzer( analyzerAnn, context );
 	}
 
-	private Analyzer getAnalyzer(org.hibernate.search.annotations.Analyzer analyzerAnn) {
+	private Analyzer getAnalyzer(org.hibernate.search.annotations.Analyzer analyzerAnn, InitContext context) {
 		Class analyzerClass = analyzerAnn == null ? void.class : analyzerAnn.impl();
 		if ( analyzerClass == void.class ) {
-			return null;
+			String definition = analyzerAnn == null ? "" : analyzerAnn.definition();
+			if ( StringHelper.isEmpty( definition ) ) {
+				return null;
+			}
+			else {
+
+				return context.buildLazyAnalyzer( definition );
+			}
 		}
 		else {
 			try {
@@ -130,7 +141,7 @@
 	}
 
 	private void initializeMembers(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix,
-								   Set<XClass> processedClasses) {
+								   Set<XClass> processedClasses, InitContext context) {
 		List<XClass> hierarchy = new ArrayList<XClass>();
 		for (XClass currClass = clazz; currClass != null; currClass = currClass.getSuperclass()) {
 			hierarchy.add( currClass );
@@ -142,23 +153,25 @@
 			 * Override the default analyzer for the properties if the class hold one
 			 * That's the reason we go down the hierarchy
 			 */
-			Analyzer analyzer = getAnalyzer( currClass );
+			Analyzer analyzer = getAnalyzer( currClass, context );
+
 			if ( analyzer != null ) {
 				propertiesMetadata.analyzer = analyzer;
 			}
+			getAnalyzerDefs(currClass, context);
 			// Check for any ClassBridges annotation.
 			ClassBridges classBridgesAnn = currClass.getAnnotation( ClassBridges.class );
 			if ( classBridgesAnn != null ) {
 				ClassBridge[] cbs = classBridgesAnn.value();
 				for (ClassBridge cb : cbs) {
-					bindClassAnnotation( prefix, propertiesMetadata, cb );
+					bindClassAnnotation( prefix, propertiesMetadata, cb, context );
 				}
 			}
 
 			// Check for any ClassBridge style of annotations.
 			ClassBridge classBridgeAnn = currClass.getAnnotation( ClassBridge.class );
 			if ( classBridgeAnn != null ) {
-				bindClassAnnotation( prefix, propertiesMetadata, classBridgeAnn );
+				bindClassAnnotation( prefix, propertiesMetadata, classBridgeAnn, context );
 			}
 
 			//Get similarity
@@ -177,12 +190,12 @@
 			// so indexing a non property does not make sense
 			List<XProperty> methods = currClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
 			for (XProperty method : methods) {
-				initializeMember( method, propertiesMetadata, isRoot, prefix, processedClasses );
+				initializeMember( method, propertiesMetadata, isRoot, prefix, processedClasses, context );
 			}
 
 			List<XProperty> fields = currClass.getDeclaredProperties( XClass.ACCESS_FIELD );
 			for (XProperty field : fields) {
-				initializeMember( field, propertiesMetadata, isRoot, prefix, processedClasses );
+				initializeMember( field, propertiesMetadata, isRoot, prefix, processedClasses, context );
 			}
 		}
 		if ( isRoot && similarityClass != null ) {
@@ -196,6 +209,17 @@
 		}
 	}
 
+	private void getAnalyzerDefs(XAnnotatedElement annotatedElement, InitContext 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 );
+	}
+
 	public String getIdentifierName() {
 		return idGetter.getName();
 	}
@@ -205,7 +229,7 @@
 	}
 
 	private void initializeMember(XProperty member, PropertiesMetadata propertiesMetadata, boolean isRoot,
-								  String prefix, Set<XClass> processedClasses) {
+								  String prefix, Set<XClass> processedClasses, InitContext context) {
 
 		DocumentId documentIdAnn = member.getAnnotation( DocumentId.class );
 		if ( documentIdAnn != null ) {
@@ -239,7 +263,7 @@
 				propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( null, member, reflectionManager ) );
 				// Field > property > entity analyzer
 				Analyzer analyzer = null; //no field analyzer
-				if ( analyzer == null ) analyzer = getAnalyzer( member );
+				if ( analyzer == null ) analyzer = getAnalyzer( member, context );
 				if ( analyzer == null ) analyzer = propertiesMetadata.analyzer;
 				if ( analyzer == null ) throw new AssertionFailure( "Analizer should not be undefined" );
 				this.analyzer.addScopedAnalyzer( fieldName, analyzer );
@@ -249,7 +273,7 @@
 			org.hibernate.search.annotations.Field fieldAnn =
 					member.getAnnotation( org.hibernate.search.annotations.Field.class );
 			if ( fieldAnn != null ) {
-				bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn );
+				bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
 			}
 		}
 		{
@@ -257,10 +281,11 @@
 					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 );
+					bindFieldAnnotation( member, propertiesMetadata, prefix, fieldAnn, context );
 				}
 			}
 		}
+		getAnalyzerDefs( member, context );
 
 		IndexedEmbedded embeddedAnn = member.getAnnotation( IndexedEmbedded.class );
 		if ( embeddedAnn != null ) {
@@ -297,10 +322,10 @@
 				propertiesMetadata.embeddedPropertiesMetadata.add( metadata );
 				metadata.boost = getBoost( member );
 				//property > entity analyzer
-				Analyzer analyzer = getAnalyzer( member );
+				Analyzer analyzer = getAnalyzer( member, context );
 				metadata.analyzer = analyzer != null ? analyzer : propertiesMetadata.analyzer;
 				String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
-				initializeMembers( elementClass, metadata, false, localPrefix, processedClasses );
+				initializeMembers( elementClass, metadata, false, localPrefix, processedClasses, context );
 				/**
 				 * We will only index the "expected" type but that's OK, HQL cannot do downcasting either
 				 */
@@ -338,7 +363,7 @@
 		}
 	}
 
-	private void bindClassAnnotation(String prefix, PropertiesMetadata propertiesMetadata, ClassBridge ann) {
+	private void bindClassAnnotation(String prefix, PropertiesMetadata propertiesMetadata, ClassBridge ann, InitContext context) {
 		//FIXME name should be prefixed
 		String fieldName = prefix + ann.name();
 		propertiesMetadata.classNames.add( fieldName );
@@ -348,13 +373,13 @@
 		propertiesMetadata.classBridges.add( BridgeFactory.extractType( ann ) );
 		propertiesMetadata.classBoosts.add( ann.boost().value() );
 
-		Analyzer analyzer = getAnalyzer( ann.analyzer() );
+		Analyzer analyzer = getAnalyzer( ann.analyzer(), context );
 		if ( analyzer == null ) analyzer = propertiesMetadata.analyzer;
 		if ( analyzer == null ) throw new AssertionFailure( "Analyzer should not be undefined" );
 		this.analyzer.addScopedAnalyzer( fieldName, analyzer );
 	}
 
-	private void bindFieldAnnotation(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, org.hibernate.search.annotations.Field fieldAnn) {
+	private void bindFieldAnnotation(XProperty member, PropertiesMetadata propertiesMetadata, String prefix, org.hibernate.search.annotations.Field fieldAnn, InitContext context) {
 		setAccessible( member );
 		propertiesMetadata.fieldGetters.add( member );
 		String fieldName = prefix + BinderHelper.getAttributeName( member, fieldAnn.name() );
@@ -365,8 +390,8 @@
 		propertiesMetadata.fieldBridges.add( BridgeFactory.guessType( fieldAnn, member, reflectionManager ) );
 
 		// Field > property > entity analyzer
-		Analyzer analyzer = getAnalyzer( fieldAnn.analyzer() );
-		if ( analyzer == null ) analyzer = getAnalyzer( member );
+		Analyzer analyzer = getAnalyzer( fieldAnn.analyzer(), context );
+		if ( analyzer == null ) analyzer = getAnalyzer( member, context );
 		if ( analyzer == null ) analyzer = propertiesMetadata.analyzer;
 		if ( analyzer == null ) throw new AssertionFailure( "Analizer should not be undefined" );
 		this.analyzer.addScopedAnalyzer( fieldName, analyzer );

Added: search/trunk/src/java/org/hibernate/search/impl/InitContext.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/InitContext.java	                        (rev 0)
+++ search/trunk/src/java/org/hibernate/search/impl/InitContext.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,193 @@
+//$
+package org.hibernate.search.impl;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.hibernate.search.annotations.AnalyzerDef;
+import org.hibernate.search.annotations.TokenizerDef;
+import org.hibernate.search.annotations.Parameter;
+import org.hibernate.search.annotations.TokenFilterDef;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.Environment;
+import org.hibernate.search.util.DelegateNamedAnalyzer;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.util.ReflectHelper;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.search.Similarity;
+import org.apache.solr.analysis.TokenizerFactory;
+import org.apache.solr.analysis.TokenizerChain;
+import org.apache.solr.analysis.TokenFilterFactory;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class InitContext {
+	private Map<String, AnalyzerDef> analyzerDefs = new HashMap<String, AnalyzerDef>();
+	private List<DelegateNamedAnalyzer> lazyAnalyzers = new ArrayList<DelegateNamedAnalyzer>();
+	private final Analyzer defaultAnalyzer;
+	private final Similarity defaultSimilarity;
+
+	public InitContext(Configuration cfg) {
+		defaultAnalyzer = initAnalyzer(cfg);
+		defaultSimilarity = initSimilarity(cfg);
+	}
+
+	public void addAnalyzerDef(AnalyzerDef ann) {
+		//FIXME somehow remember where the analyzerDef comes from and raise an exception if an analyzerDef
+		//with the same name from two different places are added
+		//multiple adding from the same place is required to deal with inheritance hierarchy processed multiple times
+		if ( ann != null && analyzerDefs.put( ann.name(), ann ) != null ) {
+			//throw new SearchException("Multiple AnalyzerDef with the same name: " + name);
+		}
+	}
+
+	public Analyzer buildLazyAnalyzer(String name) {
+		final DelegateNamedAnalyzer delegateNamedAnalyzer = new DelegateNamedAnalyzer( name );
+		lazyAnalyzers.add(delegateNamedAnalyzer);
+		return delegateNamedAnalyzer;
+	}
+
+	public List<DelegateNamedAnalyzer> getLazyAnalyzers() {
+		return lazyAnalyzers;
+	}
+
+	/**
+	 * Initilises the Lucene analyzer to use by reading the analyzer class from the configuration and instantiating it.
+	 *
+	 * @param cfg
+	 *            The current configuration.
+	 * @return The Lucene analyzer to use for tokenisation.
+	 */
+	private Analyzer initAnalyzer(Configuration cfg) {
+		Class analyzerClass;
+		String analyzerClassName = cfg.getProperty( Environment.ANALYZER_CLASS);
+		if (analyzerClassName != null) {
+			try {
+				analyzerClass = ReflectHelper.classForName(analyzerClassName);
+			} catch (Exception e) {
+				return buildLazyAnalyzer( analyzerClassName );
+//				throw new SearchException("Lucene analyzer class '" + analyzerClassName + "' defined in property '"
+//						+ Environment.ANALYZER_CLASS + "' could not be found.", e);
+			}
+		} else {
+			analyzerClass = StandardAnalyzer.class;
+		}
+		// Initialize analyzer
+		Analyzer defaultAnalyzer;
+		try {
+			defaultAnalyzer = (Analyzer) analyzerClass.newInstance();
+		} catch (ClassCastException e) {
+			throw new SearchException("Lucene analyzer does not implement " + Analyzer.class.getName() + ": "
+					+ analyzerClassName, e);
+		} catch (Exception e) {
+			throw new SearchException("Failed to instantiate lucene analyzer with type " + analyzerClassName, e);
+		}
+		return defaultAnalyzer;
+	}
+
+	/**
+	 * Initilises the Lucene similarity to use
+	 */
+	private Similarity initSimilarity(Configuration cfg) {
+		Class similarityClass;
+		String similarityClassName = cfg.getProperty(Environment.SIMILARITY_CLASS);
+		if (similarityClassName != null) {
+			try {
+				similarityClass = ReflectHelper.classForName(similarityClassName);
+			} catch (Exception e) {
+				throw new SearchException("Lucene Similarity class '" + similarityClassName + "' defined in property '"
+						+ Environment.SIMILARITY_CLASS + "' could not be found.", e);
+			}
+		}
+		else {
+			similarityClass = null;
+		}
+
+		// Initialize similarity
+		if ( similarityClass == null ) {
+			return Similarity.getDefault();
+		}
+		else {
+			Similarity defaultSimilarity;
+			try {
+				defaultSimilarity = (Similarity) similarityClass.newInstance();
+			} catch (ClassCastException e) {
+				throw new SearchException("Lucene similarity does not extend " + Similarity.class.getName() + ": "
+						+ similarityClassName, e);
+			} catch (Exception e) {
+				throw new SearchException("Failed to instantiate lucene similarity with type " + similarityClassName, e);
+			}
+			return defaultSimilarity;
+		}
+	}
+
+	public Analyzer getDefaultAnalyzer() {
+		return defaultAnalyzer;
+	}
+
+	public Similarity getDefaultSimilarity() {
+		return defaultSimilarity;
+	}
+
+	public void initLazyAnalyzers() {
+		Map<String, Analyzer> initializedAnalizers = new HashMap<String, Analyzer>( analyzerDefs.size() );
+
+		for (DelegateNamedAnalyzer namedAnalyzer : lazyAnalyzers) {
+			String name = namedAnalyzer.getName();
+			if ( initializedAnalizers.containsKey( name ) ) {
+				namedAnalyzer.setDelegate( initializedAnalizers.get( name ) );
+			}
+			else {
+				if ( analyzerDefs.containsKey( name ) ) {
+					final Analyzer analyzer = buildAnalyzer( analyzerDefs.get( name ) );
+					namedAnalyzer.setDelegate( analyzer );
+					initializedAnalizers.put( name, analyzer );
+				}
+				else {
+					//exception
+					throw new SearchException("Analyzer found with an unknown definition: " + name);
+				}
+			}
+		}
+	}
+
+	private Analyzer buildAnalyzer(AnalyzerDef analyzerDef) {
+		TokenizerDef token = analyzerDef.tokenizer();
+		TokenizerFactory tokenFactory = (TokenizerFactory) instantiate( token.factory() );
+		tokenFactory.init( getMapOfParameters( token.params() ) );
+
+		final int length = analyzerDef.filters().length;
+		TokenFilterFactory[] filters = new TokenFilterFactory[length];
+		for ( int index = 0 ; index < length ; index++ ) {
+			TokenFilterDef filterDef = analyzerDef.filters()[index];
+			filters[index] = (TokenFilterFactory) instantiate( filterDef.factory() );
+			filters[index].init( getMapOfParameters( filterDef.params() ) );
+		}
+		return new TokenizerChain(tokenFactory, filters);
+	}
+
+	private Object instantiate(Class clazz) {
+		try {
+			return clazz.newInstance();
+		}
+		catch (IllegalAccessException e) {
+			throw new SearchException( "Unable to instantiate class: " + clazz, e );
+		}
+		catch (InstantiationException e) {
+			throw new SearchException( "Unable to instantiate class: " + clazz, e );
+		}
+	}
+
+	private Map<String, String> getMapOfParameters(Parameter[] params) {
+		Map<String, String> mapOfParams = new HashMap<String, String>( params.length );
+		for (Parameter param : params) {
+			mapOfParams.put( param.name(), param.value() );
+		}
+		return Collections.unmodifiableMap( mapOfParams );
+	}
+}

Modified: search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java	2008-04-22 15:04:59 UTC (rev 14523)
+++ search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -13,9 +13,6 @@
 import java.util.WeakHashMap;
 import java.util.concurrent.locks.ReentrantLock;
 
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.search.Similarity;
 import org.hibernate.annotations.common.reflection.ReflectionManager;
 import org.hibernate.annotations.common.reflection.XClass;
 import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
@@ -46,7 +43,6 @@
 import org.hibernate.search.store.DirectoryProvider;
 import org.hibernate.search.store.DirectoryProviderFactory;
 import org.hibernate.search.store.optimization.OptimizerStrategy;
-import org.hibernate.util.ReflectHelper;
 
 /**
  * @author Emmanuel Bernard
@@ -90,11 +86,8 @@
 	public SearchFactoryImpl(Configuration cfg) {
 		//yuk
 		ReflectionManager reflectionManager = getReflectionManager( cfg );
-		//InitContext context = new InitContext();
-		//Analyzer analyzer = initAnalyzer(cfg, context);
-		Analyzer analyzer = initAnalyzer(cfg);
-		Similarity similarity = initSimilarity(cfg);
-		initDocumentBuilders(cfg, reflectionManager, analyzer, similarity);
+		InitContext context = new InitContext(cfg);
+		initDocumentBuilders(cfg, reflectionManager, context );
 
 		Set<Class> indexedClasses = documentBuilders.keySet();
 		for (DocumentBuilder builder : documentBuilders.values()) {
@@ -250,13 +243,10 @@
 		getBackendQueueProcessorFactory().getProcessor( queue ).run();
 	}
 
-	private void initDocumentBuilders(Configuration cfg, ReflectionManager reflectionManager, Analyzer analyzer, Similarity similarity) {
+	private void initDocumentBuilders(Configuration cfg, ReflectionManager reflectionManager, InitContext context) {
 		Iterator iter = cfg.getClassMappings();
 		DirectoryProviderFactory factory = new DirectoryProviderFactory();
-		//Map<String, AnalyzerDef> analyzerDefs = new HashMap<String, AnalyzerDef>();
-		
-		//cfg.getClass().getAnnotation( AnalyzerDef.class );
-		//TODO SOlr.......
+
 		while (iter.hasNext()) {
 			PersistentClass clazz = (PersistentClass) iter.next();
 			Class<?> mappedClass = clazz.getMappedClass();
@@ -267,88 +257,21 @@
 						DirectoryProviderFactory.DirectoryProviders providers = factory.createDirectoryProviders( mappedXClass, cfg, this );
 
 						final DocumentBuilder<Object> documentBuilder = new DocumentBuilder<Object>(
-								mappedXClass, analyzer, similarity, providers.getProviders(), providers.getSelectionStrategy(),
+								mappedXClass, context, providers.getProviders(), providers.getSelectionStrategy(),
 								reflectionManager
 						);
 
 						documentBuilders.put( mappedClass, documentBuilder );
 					}
 					bindFilterDefs(mappedXClass);
+					//TODO should analyzer def for classes at tyher sqme level???
 				}
 			}
 		}
+		context.initLazyAnalyzers();
 		factory.startDirectoryProviders();
 	}
 
-	/**
-	 * Initilises the Lucene analyzer to use by reading the analyzer class from the configuration and instantiating it.
-	 *
-	 * @param cfg
-	 *            The current configuration.
-	 * @return The Lucene analyzer to use for tokenisation.
-	 */
-	private Analyzer initAnalyzer(Configuration cfg) {
-		Class analyzerClass;
-		String analyzerClassName = cfg.getProperty(Environment.ANALYZER_CLASS);
-		if (analyzerClassName != null) {
-			try {
-				analyzerClass = ReflectHelper.classForName(analyzerClassName);
-			} catch (Exception e) {
-				throw new SearchException("Lucene analyzer class '" + analyzerClassName + "' defined in property '"
-						+ Environment.ANALYZER_CLASS + "' could not be found.", e);
-			}
-		} else {
-			analyzerClass = StandardAnalyzer.class;
-		}
-		// Initialize analyzer
-		Analyzer defaultAnalyzer;
-		try {
-			defaultAnalyzer = (Analyzer) analyzerClass.newInstance();
-		} catch (ClassCastException e) {
-			throw new SearchException("Lucene analyzer does not implement " + Analyzer.class.getName() + ": "
-					+ analyzerClassName, e);
-		} catch (Exception e) {
-			throw new SearchException("Failed to instantiate lucene analyzer with type " + analyzerClassName, e);
-		}
-		return defaultAnalyzer;
-	}
-
-	/**
-	 * Initilises the Lucene similarity to use
-	 */
-	private Similarity initSimilarity(Configuration cfg) {
-		Class similarityClass;
-		String similarityClassName = cfg.getProperty(Environment.SIMILARITY_CLASS);
-		if (similarityClassName != null) {
-			try {
-				similarityClass = ReflectHelper.classForName(similarityClassName);
-			} catch (Exception e) {
-				throw new SearchException("Lucene Similarity class '" + similarityClassName + "' defined in property '"
-						+ Environment.SIMILARITY_CLASS + "' could not be found.", e);
-			}
-		}
-		else {
-			similarityClass = null;
-		}
-
-		// Initialize similarity
-		if ( similarityClass == null ) {
-			return Similarity.getDefault();
-		}
-		else {
-			Similarity defaultSimilarity;
-			try {
-				defaultSimilarity = (Similarity) similarityClass.newInstance();
-			} catch (ClassCastException e) {
-				throw new SearchException("Lucene similarity does not extend " + Similarity.class.getName() + ": "
-						+ similarityClassName, e);
-			} catch (Exception e) {
-				throw new SearchException("Failed to instantiate lucene similarity with type " + similarityClassName, e);
-			}
-			return defaultSimilarity;
-		}
-	}
-
 	private void buildFilterCachingStrategy(Properties properties) {
 		String impl = properties.getProperty( Environment.FILTER_CACHING_STRATEGY );
 		if ( StringHelper.isEmpty( impl ) || "mru".equalsIgnoreCase( impl ) ) {
@@ -363,10 +286,10 @@
 				throw new SearchException( "Unable to find filterCachingStrategy class: " + impl, e );
 			}
 			catch (IllegalAccessException e) {
-				throw new SearchException( "Unable to instanciate filterCachingStrategy class: " + impl, e );
+				throw new SearchException( "Unable to instantiate filterCachingStrategy class: " + impl, e );
 			}
 			catch (InstantiationException e) {
-				throw new SearchException( "Unable to instanciate filterCachingStrategy class: " + impl, e );
+				throw new SearchException( "Unable to instantiate filterCachingStrategy class: " + impl, e );
 			}
 		}
 		filterCachingStrategy.initialize( properties );

Added: search/trunk/src/java/org/hibernate/search/util/DelegateNamedAnalyzer.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/util/DelegateNamedAnalyzer.java	                        (rev 0)
+++ search/trunk/src/java/org/hibernate/search/util/DelegateNamedAnalyzer.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,46 @@
+//$
+package org.hibernate.search.util;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.TokenStream;
+
+/**
+ * delegate to a named analyzer
+ * delegated Analyzers are lazily configured
+ *
+ * @author Emmanuel Bernard
+ */
+public class DelegateNamedAnalyzer extends Analyzer {
+	private String name;
+	private Analyzer delegate;
+
+	public DelegateNamedAnalyzer(String name) {
+		this.name = name;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setDelegate(Analyzer delegate) {
+		this.delegate = delegate;
+		this.name = null; //unique init
+	}
+
+	public TokenStream tokenStream(String fieldName, Reader reader) {
+		return delegate.tokenStream( fieldName, reader );
+	}
+
+	@Override
+	public TokenStream reusableTokenStream(String fieldName, Reader reader) throws IOException {
+		return delegate.reusableTokenStream( fieldName, reader );
+	}
+
+	@Override
+	public int getPositionIncrementGap(String fieldName) {
+		return delegate.getPositionIncrementGap( fieldName );
+	}
+}

Added: search/trunk/src/test/org/hibernate/search/test/analyzer/solr/SolrAnalyzerTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/analyzer/solr/SolrAnalyzerTest.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/analyzer/solr/SolrAnalyzerTest.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,41 @@
+package org.hibernate.search.test.analyzer.solr;
+
+import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.Transaction;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.index.Term;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class SolrAnalyzerTest extends SearchTestCase {
+	public void testAnalyzerDef() throws Exception {
+		Team team = new Team();
+		team.setDescription( "This is a Dˆscription" );
+		team.setLocation( "Atlanta" );
+		team.setName( "ATL team" );
+		FullTextSession fts = Search.createFullTextSession( openSession() );
+		Transaction tx = fts.beginTransaction();
+		fts.persist( team );
+		tx.commit();
+		fts.clear();
+		tx = fts.beginTransaction();
+		TermQuery query = new TermQuery( new Term("description", "Dˆscription") );
+		assertEquals( "iso latin filter should work", 0, fts.createFullTextQuery( query ).list().size() );
+		query = new TermQuery( new Term("description", "is") );
+		assertEquals( "stop word filter should work", 0, fts.createFullTextQuery( query ).list().size() );
+		query = new TermQuery( new Term("description", "dascription") );
+		assertEquals( 1, fts.createFullTextQuery( query ).list().size() );
+		fts.delete( fts.createFullTextQuery( query ).list().get( 0 ) );
+		tx.commit();
+		fts.close();
+	}
+
+	protected Class[] getMappings() {
+		return new Class[] {
+				Team.class
+		};
+	}
+}

Added: search/trunk/src/test/org/hibernate/search/test/analyzer/solr/Team.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/analyzer/solr/Team.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/analyzer/solr/Team.java	2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,81 @@
+package org.hibernate.search.test.analyzer.solr;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.AnalyzerDef;
+import org.hibernate.search.annotations.TokenizerDef;
+import org.hibernate.search.annotations.TokenFilterDef;
+import org.hibernate.search.annotations.Parameter;
+import org.hibernate.search.annotations.Analyzer;
+import org.apache.solr.analysis.StandardTokenizerFactory;
+import org.apache.solr.analysis.ISOLatin1AccentFilterFactory;
+import org.apache.solr.analysis.LowerCaseFilterFactory;
+import org.apache.solr.analysis.StopFilterFactory;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+ at Indexed
+ at AnalyzerDef(name="customanalyzer",
+		tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
+		filters = {
+				@TokenFilterDef(factory = ISOLatin1AccentFilterFactory.class),
+				@TokenFilterDef(factory = LowerCaseFilterFactory.class),
+				@TokenFilterDef(factory = StopFilterFactory.class, params = {
+					@Parameter(name="words", value= "org/hibernate/search/test/analyzer/solr/stoplist.properties" ),
+					@Parameter(name="ignoreCase", value="true")
+				})
+})
+public class Team {
+	@Id
+	@DocumentId
+	@GeneratedValue
+	private Integer id;
+
+	@Field
+	private String name;
+
+	@Field
+	private String location;
+
+	@Field @Analyzer(definition = "customanalyzer")
+	private String description;
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getLocation() {
+		return location;
+	}
+
+	public void setLocation(String location) {
+		this.location = location;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+}

Added: search/trunk/src/test/org/hibernate/search/test/analyzer/solr/stoplist.properties
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/analyzer/solr/stoplist.properties	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/analyzer/solr/stoplist.properties	2008-04-23 02:31:14 UTC (rev 14524)
@@ -0,0 +1,33 @@
+a
+an
+and
+are
+as
+at
+be
+but
+by
+for
+if
+in
+into
+is
+it
+no
+not
+of
+on
+or
+such
+that
+the
+their
+then
+there
+these
+they
+this
+to
+was
+will
+with
\ No newline at end of file




More information about the hibernate-commits mailing list