/* * Copyright 2004-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.compass.annotations.config.binding; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.analysis.Analyzer; import org.compass.annotations.*; import org.compass.core.config.CommonMetaDataLookup; import org.compass.core.config.CompassConfigurable; import org.compass.core.config.CompassEnvironment; import org.compass.core.config.CompassSettings; import org.compass.core.config.ConfigurationException; import org.compass.core.config.binding.AbstractClassMetaDataMappingBinding; import org.compass.core.config.binding.metadata.ClassMetaData; import org.compass.core.converter.Converter; import org.compass.core.converter.mapping.support.FormatDelegateConverter; import org.compass.core.engine.naming.StaticPropertyPath; import org.compass.core.engine.subindex.ConstantSubIndexHash; import org.compass.core.engine.subindex.SubIndexHash; import org.compass.core.lucene.LuceneEnvironment; import org.compass.core.mapping.AliasMapping; import org.compass.core.mapping.MappingException; import org.compass.core.mapping.SpellCheck; import org.compass.core.mapping.internal.DefaultAllMapping; import org.compass.core.mapping.internal.InternalCascadeMapping; import org.compass.core.mapping.internal.InternalCompassMapping; import org.compass.core.mapping.internal.InternalMapping; import org.compass.core.mapping.internal.InternalResourcePropertyMapping; import org.compass.core.mapping.osem.ClassBoostPropertyMapping; import org.compass.core.mapping.osem.ClassDynamicPropertyMapping; import org.compass.core.mapping.osem.ClassIdPropertyMapping; import org.compass.core.mapping.osem.ClassMapping; import org.compass.core.mapping.osem.ClassPropertyAnalyzerController; import org.compass.core.mapping.osem.ClassPropertyMapping; import org.compass.core.mapping.osem.ClassPropertyMetaDataMapping; import org.compass.core.mapping.osem.ComponentMapping; import org.compass.core.mapping.osem.ConstantMetaDataMapping; import org.compass.core.mapping.osem.DynamicMetaDataMapping; import org.compass.core.mapping.osem.IdComponentMapping; import org.compass.core.mapping.osem.ParentMapping; import org.compass.core.mapping.osem.PlainCascadeMapping; import org.compass.core.mapping.osem.ReferenceMapping; import org.compass.core.mapping.osem.internal.InternalObjectMapping; import org.compass.core.metadata.Alias; import org.compass.core.metadata.CompassMetaData; import org.compass.core.util.ClassUtils; import org.compass.core.util.StringUtils; /** * @author kimchy */ public class AnnotationsMappingBinding extends AbstractClassMetaDataMappingBinding { public static final Log log = LogFactory.getLog(AnnotationsMappingBinding.class); private static final Class[] applicableAnnotations = new Class[]{Searchable.class, SearchConverter.class, SearchConverters.class, SearchAnalyzer.class, SearchAnalyzers.class, SearchAnalyzerFilter.class, SearchAnalyzerFilters.class}; private CommonMetaDataLookup valueLookup; private InternalCompassMapping mapping; private ClassMapping classMapping; private CompassSettings settings; public void setUpBinding(InternalCompassMapping mapping, CompassMetaData metaData, CompassSettings settings) { super.setUpBinding(mapping, metaData, settings); this.mapping = mapping; this.valueLookup = new CommonMetaDataLookup(metaData); this.settings = settings; } protected boolean isApplicable(ClassMetaData classMetaData) { for (Class applicableAnnotation : applicableAnnotations) { if (classMetaData.hasAnnotation(applicableAnnotation.getName())) return true; } return false; } public boolean addPackage(String packageName) throws ConfigurationException, MappingException { Package pckg; try { pckg = ClassUtils.forName(packageName + ".package-info", settings.getClassLoader()).getPackage(); } catch (ClassNotFoundException e) { return false; } return processNonSearchableAnnotations(pckg); } public boolean addClass(Class clazz) throws ConfigurationException, MappingException { Class<?> annotationClass = clazz; boolean found = processNonSearchableAnnotations(clazz); Searchable searchable = annotationClass.getAnnotation(Searchable.class); if (searchable == null) { return found; } String alias = getAliasFromSearchableClass(clazz, searchable); // try and check is the class mapping is already defined // if it is, we will add mapping definitions on top of it boolean newClassMapping = false; AliasMapping aliasMapping = mapping.getAliasMapping(alias); if (aliasMapping != null) { if (!(aliasMapping instanceof ClassMapping)) { throw new MappingException("Defined searchable annotation on a class with alias [" + alias + "] which" + " has other mapping definitions, but it not of type class mapping"); } classMapping = (ClassMapping) aliasMapping; } else { classMapping = new ClassMapping(); newClassMapping = true; } classMapping.setAlias(alias); classMapping.setName(clazz.getName()); classMapping.setClazz(clazz); // sub index (including hash support) String subIndex = searchable.subIndex(); if (!StringUtils.hasLength(subIndex)) { subIndex = alias; } SearchableSubIndexHash searchableSubIndexHash = annotationClass.getAnnotation(SearchableSubIndexHash.class); if (searchableSubIndexHash == null) { classMapping.setSubIndexHash(new ConstantSubIndexHash(subIndex)); } else { SubIndexHash subIndexHash; try { subIndexHash = searchableSubIndexHash.value().newInstance(); } catch (Exception e) { throw new MappingException("Failed to create sub index hash [" + searchableSubIndexHash.value().getName() + "]", e); } if (subIndexHash instanceof CompassConfigurable) { CompassSettings settings = new CompassSettings(); for (int i = 0; i < searchableSubIndexHash.settings().length; i++) { SearchSetting setting = searchableSubIndexHash.settings()[i]; settings.setSetting(setting.name(), setting.value()); } ((CompassConfigurable) subIndexHash).configure(settings); } classMapping.setSubIndexHash(subIndexHash); } if (log.isTraceEnabled()) { log.trace("Alias [" + classMapping.getAlias() + "] is mapped to sub index hash [" + classMapping.getSubIndexHash() + "]"); } DefaultAllMapping allMapping = new DefaultAllMapping(); SearchableAllMetaData allMetaData = annotationClass.getAnnotation(SearchableAllMetaData.class); if (allMetaData != null) { if (allMetaData.enable() == EnableAll.TRUE) { allMapping.setSupported(true); } else if (allMetaData.enable() == EnableAll.FALSE) { allMapping.setSupported(false); } if (allMetaData.excludeAlias() == ExcludeAlias.TRUE) { allMapping.setExcludeAlias(true); } else if (allMetaData.excludeAlias() == ExcludeAlias.FALSE) { allMapping.setExcludeAlias(false); } if (StringUtils.hasLength(allMetaData.name())) { allMapping.setProperty(allMetaData.name()); } if (allMetaData.termVector() != TermVector.NA) { allMapping.setTermVector(AnnotationsBindingUtils.convert(allMetaData.termVector())); } if (allMetaData.spellCheck() == org.compass.annotations.SpellCheck.EXCLUDE) { allMapping.setSpellCheck(SpellCheck.EXCLUDE); } else if (allMetaData.spellCheck() == org.compass.annotations.SpellCheck.INCLUDE) { allMapping.setSpellCheck(SpellCheck.INCLUDE); } else if (allMetaData.spellCheck() == org.compass.annotations.SpellCheck.NA) { allMapping.setSpellCheck(SpellCheck.NA); } if (allMetaData.includePropertiesWithNoMappings() == NABoolean.TRUE) { allMapping.setIncludePropertiesWithNoMappings(true); } else if (allMetaData.includePropertiesWithNoMappings() == NABoolean.FALSE) { allMapping.setIncludePropertiesWithNoMappings(false); } allMapping.setOmitNorms(AnnotationsBindingUtils.convert(allMetaData.omitNorms())); allMapping.setOmitTf(AnnotationsBindingUtils.convert(allMetaData.omitTf())); } classMapping.setAllMapping(allMapping); if (searchable.spellCheck() == org.compass.annotations.SpellCheck.NA) { classMapping.setSpellCheck(SpellCheck.NA); } else if (searchable.spellCheck() == org.compass.annotations.SpellCheck.EXCLUDE) { classMapping.setSpellCheck(SpellCheck.EXCLUDE); } else if (searchable.spellCheck() == org.compass.annotations.SpellCheck.INCLUDE) { classMapping.setSpellCheck(SpellCheck.INCLUDE); } classMapping.setBoost(searchable.boost()); classMapping.setRoot(searchable.root()); classMapping.setPoly(searchable.poly()); if (!Object.class.equals(searchable.polyClass())) { classMapping.setPolyClass(searchable.polyClass()); } if (StringUtils.hasLength(searchable.analyzer())) { classMapping.setAnalyzer(searchable.analyzer()); } if (searchable.supportUnmarshall() == SupportUnmarshall.TRUE) { classMapping.setSupportUnmarshall(true); } else if (searchable.supportUnmarshall() == SupportUnmarshall.FALSE) { classMapping.setSupportUnmarshall(false); } if (searchable.filterDuplicates() == FilterDuplicates.TRUE) { classMapping.setFilterDuplicates(true); } else if (searchable.filterDuplicates() == FilterDuplicates.FALSE) { classMapping.setFilterDuplicates(false); } classMapping.setManagedId(AnnotationsBindingUtils.convert(searchable.managedId())); bindConverter(classMapping, searchable.converter()); processAnnotatedClass(annotationClass); if (newClassMapping) { mapping.addMapping(classMapping); } classMapping = null; return true; } private boolean processNonSearchableAnnotations(AnnotatedElement annotatedElement) { boolean found = false; if (annotatedElement.isAnnotationPresent(SearchConverter.class)) { found = true; bindConverter(annotatedElement.getAnnotation(SearchConverter.class)); } if (annotatedElement.isAnnotationPresent(SearchConverters.class)) { found = true; SearchConverters searchConverters = annotatedElement.getAnnotation(SearchConverters.class); for (SearchConverter searchConverter : searchConverters.value()) { bindConverter(searchConverter); } } if (annotatedElement.isAnnotationPresent(SearchAnalyzer.class)) { found = true; bindAnalyzer(annotatedElement.getAnnotation(SearchAnalyzer.class)); } if (annotatedElement.isAnnotationPresent(SearchAnalyzers.class)) { found = true; SearchAnalyzers searchAnalyzers = annotatedElement.getAnnotation(SearchAnalyzers.class); for (SearchAnalyzer searchAnalyzer : searchAnalyzers.value()) { bindAnalyzer(searchAnalyzer); } } if (annotatedElement.isAnnotationPresent(SearchAnalyzerFilter.class)) { found = true; bindAnalyzerFilter(annotatedElement.getAnnotation(SearchAnalyzerFilter.class)); } if (annotatedElement.isAnnotationPresent(SearchAnalyzerFilters.class)) { found = true; SearchAnalyzerFilters searchAnalyzerFilters = annotatedElement.getAnnotation(SearchAnalyzerFilters.class); for (SearchAnalyzerFilter searchAnalyzerFilter : searchAnalyzerFilters.value()) { bindAnalyzerFilter(searchAnalyzerFilter); } } return found; } private void bindAnalyzerFilter(SearchAnalyzerFilter searchAnalyzerFilter) throws ConfigurationException, MappingException { ArrayList<String> settingsNames = new ArrayList<String>(); ArrayList<String> settingsValues = new ArrayList<String>(); settingsNames.add(LuceneEnvironment.AnalyzerFilter.TYPE); settingsValues.add(searchAnalyzerFilter.type().getName()); for (SearchSetting setting : searchAnalyzerFilter.settings()) { settingsNames.add(setting.name()); settingsValues.add(setting.value()); } settings.setGroupSettings(LuceneEnvironment.AnalyzerFilter.PREFIX, searchAnalyzerFilter.name(), settingsNames.toArray(new String[settingsNames.size()]), settingsValues.toArray(new String[settingsValues.size()])); } private void bindAnalyzer(SearchAnalyzer searchAnalyzer) throws ConfigurationException, MappingException { ArrayList<String> settingsNames = new ArrayList<String>(); ArrayList<String> settingsValues = new ArrayList<String>(); settingsNames.add(LuceneEnvironment.Analyzer.TYPE); if (searchAnalyzer.type() == AnalyzerType.CustomAnalyzer) { if (Analyzer.class.equals(searchAnalyzer.analyzerClass())) { throw new ConfigurationException("SearchableAnalyzer [" + searchAnalyzer.name() + "] has " + "type of [" + AnalyzerType.CustomAnalyzer + "] but does not set analyzerClass"); } settingsValues.add(searchAnalyzer.analyzerClass().getName()); } else { settingsValues.add(searchAnalyzer.type().toString()); } if (searchAnalyzer.type() == AnalyzerType.Snowball) { settingsNames.add(LuceneEnvironment.Analyzer.Snowball.NAME_TYPE); settingsValues.add(searchAnalyzer.snowballType().toString()); } if (searchAnalyzer.stopWords().length > 0) { StringBuffer sb = new StringBuffer(); if (searchAnalyzer.addStopWords()) { sb.append("+"); } for (String stopword : searchAnalyzer.stopWords()) { sb.append(stopword).append(","); } settingsNames.add(LuceneEnvironment.Analyzer.STOPWORDS); settingsValues.add(sb.toString()); } if (searchAnalyzer.filters().length > 0) { StringBuffer sb = new StringBuffer(); for (String filter : searchAnalyzer.filters()) { sb.append(filter).append(","); } settingsNames.add(LuceneEnvironment.Analyzer.FILTERS); settingsValues.add(sb.toString()); } for (SearchSetting setting : searchAnalyzer.settings()) { settingsNames.add(setting.name()); settingsValues.add(setting.value()); } settings.setGroupSettings(LuceneEnvironment.Analyzer.PREFIX, searchAnalyzer.name(), settingsNames.toArray(new String[settingsNames.size()]), settingsValues.toArray(new String[settingsValues.size()])); } private void bindConverter(SearchConverter searchConverter) throws ConfigurationException, MappingException { String[] settingsNames = new String[searchConverter.settings().length + 2]; String[] settingsValues = new String[searchConverter.settings().length + 2]; int i = 0; for (; i < searchConverter.settings().length; i++) { SearchSetting setting = searchConverter.settings()[i]; settingsNames[i] = setting.name(); settingsValues[i] = setting.value(); } settingsNames[i] = CompassEnvironment.Converter.TYPE; settingsValues[i] = searchConverter.type().getName(); settingsNames[++i] = CompassEnvironment.Converter.REGISTER_CLASS; if (!searchConverter.registerClass().equals(Object.class)) { settingsValues[i] = searchConverter.registerClass().getName(); } // else, just leave it as null settings.setGroupSettings(CompassEnvironment.Converter.PREFIX, searchConverter.name(), settingsNames, settingsValues); } private String getAliasFromSearchableClass(Class clazz, Searchable searchable) { String alias = searchable.alias(); if (!StringUtils.hasLength(alias)) { alias = ClassUtils.getShortName(clazz); } else { // check if it is a lookp alias Alias aliasLookup = valueLookup.lookupAlias(alias); if (aliasLookup != null) { alias = aliasLookup.getName(); } } return alias; } /** * Recursivly process the class to find all it's annotations. Lower level * class/interfaces with annotations will be added first. */ private void processAnnotatedClass(Class<?> clazz) { if (clazz.equals(Class.class)) { return; } Class<?> superClazz = clazz.getSuperclass(); if (superClazz != null && !superClazz.equals(Object.class)) { processAnnotatedClass(superClazz); } Class<?>[] interfaces = clazz.getInterfaces(); for (Class<?> anInterface : interfaces) { processAnnotatedClass(anInterface); } SearchableConstant searchableConstant = clazz.getAnnotation(SearchableConstant.class); if (searchableConstant != null) { bindConstantMetaData(searchableConstant); } SearchableConstants searchableConstants = clazz.getAnnotation(SearchableConstants.class); if (searchableConstants != null) { for (SearchableConstant metaData : searchableConstants.value()) { bindConstantMetaData(metaData); } } SearchableDynamicMetaData searchableDynamicMetaData = clazz.getAnnotation(SearchableDynamicMetaData.class); if (searchableDynamicMetaData != null) { bindDynamicMetaData(searchableDynamicMetaData); } SearchableDynamicMetaDatas searchableDynamicMetaDatas = clazz.getAnnotation(SearchableDynamicMetaDatas.class); if (searchableDynamicMetaDatas != null) { for (SearchableDynamicMetaData metaData : searchableDynamicMetaDatas.value()) { bindDynamicMetaData(metaData); } } // handles recursive extends and the original extend if (clazz.isAnnotationPresent(Searchable.class)) { Searchable searchable = clazz.getAnnotation(Searchable.class); String[] extend = searchable.extend(); if (extend.length != 0) { ArrayList<String> extendedMappings = new ArrayList<String>(); if (classMapping.getExtendedAliases() != null) { extendedMappings.addAll(Arrays.asList(classMapping.getExtendedAliases())); } for (String extendedAlias : extend) { Alias extendedAliasLookup = valueLookup.lookupAlias(extendedAlias); if (extendedAliasLookup == null) { extendedMappings.add(extendedAlias); } else { extendedMappings.add(extendedAliasLookup.getName()); } } classMapping.setExtendedAliases(extendedMappings.toArray(new String[extendedMappings.size()])); } } // if the super class has Searchable annotation as well, add it to the list of extends ArrayList<Class> extendedClasses = new ArrayList<Class>(); if (clazz.getSuperclass() != null) { extendedClasses.add(clazz.getSuperclass()); } extendedClasses.addAll(Arrays.asList(clazz.getInterfaces())); for (Class<?> superClass : extendedClasses) { if (!superClass.isAnnotationPresent(Searchable.class)) { continue; } Searchable superSearchable = superClass.getAnnotation(Searchable.class); String alias = getAliasFromSearchableClass(superClass, superSearchable); HashSet<String> extendedMappings = new HashSet<String>(); if (classMapping.getExtendedAliases() != null) { extendedMappings.addAll(Arrays.asList(classMapping.getExtendedAliases())); } extendedMappings.add(alias); classMapping.setExtendedAliases(extendedMappings.toArray(new String[extendedMappings.size()])); } for (Field field : clazz.getDeclaredFields()) { for (Annotation annotation : field.getAnnotations()) { processsAnnotatedElement(clazz, field.getName(), "field", field.getType(), field.getGenericType(), annotation, field); } } for (Method method : clazz.getDeclaredMethods()) { if (!method.isSynthetic() && !method.isBridge() && !Modifier.isStatic(method.getModifiers()) && method.getParameterTypes().length == 0 && method.getReturnType() != void.class && (method.getName().startsWith("get") || method.getName().startsWith("is"))) { for (Annotation annotation : method.getAnnotations()) { processsAnnotatedElement(clazz, ClassUtils.getShortNameForMethod(method), "property", method.getReturnType(), method.getGenericReturnType(), annotation, method); } } } } private void processsAnnotatedElement(Class<?> searchableClass, String name, String accessor, Class<?> clazz, Type type, Annotation annotation, AnnotatedElement annotatedElement) { if (annotation instanceof SearchableId) { ClassIdPropertyMapping classPropertyMapping = new ClassIdPropertyMapping(); SearchableId searchableId = (SearchableId) annotation; bindObjectMapping(classPropertyMapping, accessor, name, searchableId.accessor(), searchableClass); bindClassPropertyIdMapping(searchableId, classPropertyMapping, clazz, type, annotatedElement); classMapping.addMapping(classPropertyMapping); } else if (annotation instanceof SearchableIdComponent) { IdComponentMapping componentMapping = new IdComponentMapping(); SearchableIdComponent searchableComponent = (SearchableIdComponent) annotation; bindObjectMapping(componentMapping, accessor, name, searchableComponent.accessor(), searchableClass); bindComponent(searchableComponent, componentMapping, clazz, type); classMapping.addMapping(componentMapping); } else if (annotation instanceof SearchableProperty) { ClassPropertyMapping classPropertyMapping = new ClassPropertyMapping(); SearchableProperty searchableProperty = (SearchableProperty) annotation; bindObjectMapping(classPropertyMapping, accessor, name, searchableProperty.accessor(), searchableClass); bindClassPropertyMapping(searchableProperty, classPropertyMapping, annotatedElement, clazz, type); classMapping.addMapping(classPropertyMapping); } else if (annotation instanceof SearchableDynamicProperty) { ClassDynamicPropertyMapping dynamicPropertyMapping = new ClassDynamicPropertyMapping(); SearchableDynamicProperty searchableDynamicProperty = (SearchableDynamicProperty) annotation; bindObjectMapping(dynamicPropertyMapping, accessor, name, searchableDynamicProperty.accessor(), searchableClass); bindClassDynamicPropertyMapping(searchableDynamicProperty, dynamicPropertyMapping, annotatedElement, clazz, type); classMapping.addMapping(dynamicPropertyMapping); } else if (annotation instanceof SearchableComponent) { ComponentMapping componentMapping = new ComponentMapping(); SearchableComponent searchableComponent = (SearchableComponent) annotation; bindObjectMapping(componentMapping, accessor, name, searchableComponent.accessor(), searchableClass); bindComponent(searchableComponent, componentMapping, clazz, type); classMapping.addMapping(componentMapping); } else if (annotation instanceof SearchableReference) { ReferenceMapping referenceMapping = new ReferenceMapping(); SearchableReference searchableReference = (SearchableReference) annotation; bindObjectMapping(referenceMapping, accessor, name, searchableReference.accessor(), searchableClass); bindReference(searchableReference, referenceMapping, clazz, type); classMapping.addMapping(referenceMapping); } else if (annotation instanceof SearchableAnalyzerProperty) { ClassPropertyAnalyzerController analyzerMapping = new ClassPropertyAnalyzerController(); SearchableAnalyzerProperty searchableAnalyzerProperty = (SearchableAnalyzerProperty) annotation; bindObjectMapping(analyzerMapping, accessor, name, searchableAnalyzerProperty.accessor(), searchableClass); bindAnalyzer(searchableAnalyzerProperty, analyzerMapping, clazz, type); classMapping.addMapping(analyzerMapping); } else if (annotation instanceof SearchableBoostProperty) { ClassBoostPropertyMapping boostPropertyMapping = new ClassBoostPropertyMapping(); SearchableBoostProperty searchableBoostProperty = (SearchableBoostProperty) annotation; bindObjectMapping(boostPropertyMapping, accessor, name, searchableBoostProperty.accessor(), searchableClass); bindBoost(searchableBoostProperty, boostPropertyMapping, clazz, type); classMapping.addMapping(boostPropertyMapping); } else if (annotation instanceof SearchableParent) { ParentMapping parentMapping = new ParentMapping(); SearchableParent searchableParent = (SearchableParent) annotation; bindObjectMapping(parentMapping, accessor, name, searchableParent.accessor(), searchableClass); bindParent(searchableParent, parentMapping, clazz, type); classMapping.addMapping(parentMapping); } else if (annotation instanceof SearchableCascading) { PlainCascadeMapping cascadeMapping = new PlainCascadeMapping(); SearchableCascading searchableCascading = (SearchableCascading) annotation; bindObjectMapping(cascadeMapping, accessor, name, searchableCascading.accessor(), searchableClass); bindCascade(searchableCascading, cascadeMapping, clazz, type); classMapping.addMapping(cascadeMapping); } else if ((annotation instanceof SearchableMetaData) || (annotation instanceof SearchableMetaDatas)) { if (!annotatedElement.isAnnotationPresent(SearchableProperty.class) && !annotatedElement.isAnnotationPresent(SearchableId.class)) { throw new MappingException("SearchableMetaData/s annotation exists without a SearchableProperty/Id, it will be ignored"); } } } private void bindCascade(SearchableCascading searchableCascading, PlainCascadeMapping cascadeMapping, Class<?> clazz, Type type) { bindConverter(cascadeMapping, searchableCascading.converter(), clazz, type); bindCascades(searchableCascading.cascade(), cascadeMapping); } private void bindParent(SearchableParent searchableParent, ParentMapping parentMapping, Class<?> clazz, Type type) { bindConverter(parentMapping, searchableParent.converter(), clazz, type); bindCascades(searchableParent.cascade(), parentMapping); } private void bindBoost(SearchableBoostProperty searchableBoostProperty, ClassBoostPropertyMapping boostPropertyMapping, Class<?> clazz, Type type) { bindConverter(boostPropertyMapping, searchableBoostProperty.converter(), clazz, type); boostPropertyMapping.setDefaultBoost(searchableBoostProperty.defaultValue()); } private void bindAnalyzer(SearchableAnalyzerProperty searchableAnalyzerProperty, ClassPropertyAnalyzerController analyzerMapping, Class<?> clazz, Type type) { bindConverter(analyzerMapping, searchableAnalyzerProperty.converter(), clazz, type); if (StringUtils.hasLength(searchableAnalyzerProperty.nullAnalyzer())) { analyzerMapping.setNullAnalyzer(searchableAnalyzerProperty.nullAnalyzer()); } } private void bindReference(SearchableReference searchableReference, ReferenceMapping referenceMapping, Class<?> clazz, Type type) { bindConverter(referenceMapping, searchableReference.converter(), clazz, type); if (StringUtils.hasLength(searchableReference.refAlias())) { referenceMapping.setRefAliases(getAliases(searchableReference.refAlias())); } else { referenceMapping.setRefClass(AnnotationsBindingUtils.getCollectionParameterClass(clazz, type)); } if (StringUtils.hasLength(searchableReference.refComponentAlias())) { referenceMapping.setRefCompAlias(searchableReference.refComponentAlias()); } if (searchableReference.lazy() == Lazy.FALSE) { referenceMapping.setLazy(false); } else if (searchableReference.lazy() == Lazy.TRUE) { referenceMapping.setLazy(true); } bindCascades(searchableReference.cascade(), referenceMapping); } private void bindComponent(SearchableComponent searchableComponent, ComponentMapping componentMapping, Class<?> clazz, Type type) { bindConverter(componentMapping, searchableComponent.converter(), clazz, type); if (StringUtils.hasLength(searchableComponent.refAlias())) { componentMapping.setRefAliases(getAliases(searchableComponent.refAlias())); } else { componentMapping.setRefClass(AnnotationsBindingUtils.getCollectionParameterClass(clazz, type)); } componentMapping.setMaxDepth(searchableComponent.maxDepth()); if (StringUtils.hasText(searchableComponent.prefix())) { componentMapping.setPrefix(searchableComponent.prefix()); } componentMapping.setOverrideByName(searchableComponent.override()); bindCascades(searchableComponent.cascade(), componentMapping); } private void bindComponent(SearchableIdComponent searchableComponent, ComponentMapping componentMapping, Class<?> clazz, Type type) { bindConverter(componentMapping, searchableComponent.converter(), clazz, type); if (StringUtils.hasLength(searchableComponent.refAlias())) { componentMapping.setRefAliases(getAliases(searchableComponent.refAlias())); } else { componentMapping.setRefClass(AnnotationsBindingUtils.getCollectionParameterClass(clazz, type)); } componentMapping.setMaxDepth(searchableComponent.maxDepth()); if (StringUtils.hasText(searchableComponent.prefix())) { componentMapping.setPrefix(searchableComponent.prefix()); } componentMapping.setOverrideByName(searchableComponent.override()); bindCascades(searchableComponent.cascade(), componentMapping); } private void bindCascades(Cascade[] cascades, InternalCascadeMapping cascadeMapping) { if (cascades == null || cascades.length == 0) { return; } org.compass.core.mapping.Cascade[] mappingCascades = new org.compass.core.mapping.Cascade[cascades.length]; for (int i = 0; i < cascades.length; i++) { mappingCascades[i] = AnnotationsBindingUtils.convert(cascades[i]); } cascadeMapping.setCascades(mappingCascades); } /** * Need to be almost exactly as <code>bindClassPropertyMapping</code>. */ private void bindClassPropertyIdMapping(SearchableId searchableProp, ClassIdPropertyMapping classPropertyMapping, Class<?> clazz, Type type, AnnotatedElement annotatedElement) throws MappingException { bindConverter(classPropertyMapping, searchableProp.idConverter()); // No need for type in property id, since it will not be a collection classPropertyMapping.setBoost(searchableProp.boost()); classPropertyMapping.setManagedId(AnnotationsBindingUtils.convert(searchableProp.managedId())); classPropertyMapping.setManagedIdIndex(AnnotationsBindingUtils.convert(searchableProp.managedIdIndex())); classPropertyMapping.setOverrideByName(searchableProp.override()); SearchableMetaData metaData = annotatedElement.getAnnotation(SearchableMetaData.class); SearchableMetaDatas metaDatas = annotatedElement.getAnnotation(SearchableMetaDatas.class); if (StringUtils.hasLength(searchableProp.converter())) { classPropertyMapping.setManagedIdConverterName(searchableProp.converter()); } else { classPropertyMapping.setManagedIdConverter(getConverter(clazz, type)); } // check if we need to create a metadata because of the SearchId // here we differ from the searchProperty mapping, since it is perfectly // fine not to create one if there are no meta-data definitions (an internal // one will be created during the process phase) if (StringUtils.hasLength(searchableProp.name())) { ClassPropertyMetaDataMapping mdMapping = new ClassPropertyMetaDataMapping(); String name = searchableProp.name(); if (!StringUtils.hasLength(name)) { name = classPropertyMapping.getName(); } mdMapping.setName(valueLookup.lookupMetaDataName(name)); mdMapping.setPath(new StaticPropertyPath(mdMapping.getName())); mdMapping.setBoost(classPropertyMapping.getBoost()); mdMapping.setAccessor(classPropertyMapping.getAccessor()); mdMapping.setPropertyName(classPropertyMapping.getPropertyName()); bindConverter(mdMapping, searchableProp.converter(), clazz, type); bindSpellCheck(mdMapping, searchableProp.spellCheck()); mdMapping.setStore(AnnotationsBindingUtils.convert(searchableProp.store())); mdMapping.setIndex(AnnotationsBindingUtils.convert(searchableProp.index())); mdMapping.setTermVector(AnnotationsBindingUtils.convert(searchableProp.termVector())); mdMapping.setOmitNorms(AnnotationsBindingUtils.convert(searchableProp.omitNorms())); mdMapping.setOmitTf(AnnotationsBindingUtils.convert(searchableProp.omitTf())); mdMapping.setReverse(AnnotationsBindingUtils.convert(searchableProp.reverse())); handleFormat(mdMapping, name, searchableProp.format()); if (StringUtils.hasLength(searchableProp.analyzer())) { mdMapping.setAnalyzer(searchableProp.analyzer()); } else { mdMapping.setAnalyzer(classMapping.getAnalyzer()); } mdMapping.setExcludeFromAll(AnnotationsBindingUtils.convert(searchableProp.excludeFromAll())); classPropertyMapping.addMapping(mdMapping); } if (metaData != null) { bindMetaData(metaData, classPropertyMapping, clazz, type); } if (metaDatas != null) { for (SearchableMetaData searchableMetaData : metaDatas.value()) { bindMetaData(searchableMetaData, classPropertyMapping, clazz, type); } } } private void bindClassDynamicPropertyMapping(SearchableDynamicProperty searchableDynamicProperty, ClassDynamicPropertyMapping dynamicPropertyMapping, AnnotatedElement annotatedElement, Class<?> clazz, Type type) { bindConverter(dynamicPropertyMapping, searchableDynamicProperty.converter(), clazz, type); dynamicPropertyMapping.setOverrideByName(searchableDynamicProperty.override()); if (StringUtils.hasText(searchableDynamicProperty.namePrefix())) { dynamicPropertyMapping.setNamePrefix(searchableDynamicProperty.namePrefix()); } if (StringUtils.hasText(searchableDynamicProperty.nameProperty())) { dynamicPropertyMapping.setNameProperty(searchableDynamicProperty.nameProperty()); } if (StringUtils.hasText(searchableDynamicProperty.valueProperty())) { dynamicPropertyMapping.setValueProperty(searchableDynamicProperty.valueProperty()); } if (StringUtils.hasText(searchableDynamicProperty.nameFormat())) { dynamicPropertyMapping.setNameFormat(searchableDynamicProperty.nameFormat()); } if (StringUtils.hasText(searchableDynamicProperty.valueFormat())) { dynamicPropertyMapping.setValueFormat(searchableDynamicProperty.valueFormat()); } if (StringUtils.hasText(searchableDynamicProperty.nameConverter())) { dynamicPropertyMapping.setNameConverterName(searchableDynamicProperty.nameConverter()); } if (StringUtils.hasText(searchableDynamicProperty.valueConverter())) { dynamicPropertyMapping.setValueConverterName(searchableDynamicProperty.valueConverter()); } dynamicPropertyMapping.getResourcePropertyMapping().setBoost(searchableDynamicProperty.boost()); dynamicPropertyMapping.getResourcePropertyMapping().setStore(AnnotationsBindingUtils.convert(searchableDynamicProperty.store())); dynamicPropertyMapping.getResourcePropertyMapping().setIndex(AnnotationsBindingUtils.convert(searchableDynamicProperty.index())); dynamicPropertyMapping.getResourcePropertyMapping().setTermVector(AnnotationsBindingUtils.convert(searchableDynamicProperty.termVector())); dynamicPropertyMapping.getResourcePropertyMapping().setOmitNorms(AnnotationsBindingUtils.convert(searchableDynamicProperty.omitNorms())); dynamicPropertyMapping.getResourcePropertyMapping().setOmitTf(AnnotationsBindingUtils.convert(searchableDynamicProperty.omitTf())); if (StringUtils.hasLength(searchableDynamicProperty.nullValue())) { dynamicPropertyMapping.getResourcePropertyMapping().setNullValue(searchableDynamicProperty.nullValue()); } } /** * Need to be almost exactly as <code>bindClassPropertyIdMapping</code>. */ private void bindClassPropertyMapping(SearchableProperty searchableProp, ClassPropertyMapping classPropertyMapping, AnnotatedElement annotatedElement, Class<?> clazz, Type type) throws MappingException { bindConverter(classPropertyMapping, searchableProp.propertyConverter()); if (!searchableProp.type().equals(Object.class)) { classPropertyMapping.setClassName(searchableProp.type().getName()); } else { // check if it is a colleciton. If it is, try to infer the // type using generics classPropertyMapping.setClassName(AnnotationsBindingUtils.getCollectionParameterClassName(clazz, type)); } if (StringUtils.hasLength(searchableProp.converter())) { classPropertyMapping.setManagedIdConverterName(searchableProp.converter()); } else { classPropertyMapping.setManagedIdConverter(getConverter(clazz, type)); } classPropertyMapping.setBoost(searchableProp.boost()); classPropertyMapping.setManagedId(AnnotationsBindingUtils.convert(searchableProp.managedId())); classPropertyMapping.setManagedIdIndex(AnnotationsBindingUtils.convert(searchableProp.managedIdIndex())); classPropertyMapping.setOverrideByName(searchableProp.override()); SearchableMetaData metaData = annotatedElement.getAnnotation(SearchableMetaData.class); SearchableMetaDatas metaDatas = annotatedElement.getAnnotation(SearchableMetaDatas.class); boolean hasMetaDataAnnotations = metaData != null || metaDatas != null; // check if we need to create a metadata because of the SearchProperty if (StringUtils.hasLength(searchableProp.name()) || !hasMetaDataAnnotations) { ClassPropertyMetaDataMapping mdMapping = new ClassPropertyMetaDataMapping(); String name = searchableProp.name(); if (!StringUtils.hasLength(name)) { name = classPropertyMapping.getName(); } mdMapping.setName(valueLookup.lookupMetaDataName(name)); mdMapping.setPath(new StaticPropertyPath(mdMapping.getName())); mdMapping.setBoost(classPropertyMapping.getBoost()); bindConverter(mdMapping, searchableProp.converter(), clazz, type); bindSpellCheck(mdMapping, searchableProp.spellCheck()); mdMapping.setAccessor(classPropertyMapping.getAccessor()); mdMapping.setPropertyName(classPropertyMapping.getPropertyName()); mdMapping.setStore(AnnotationsBindingUtils.convert(searchableProp.store())); mdMapping.setIndex(AnnotationsBindingUtils.convert(searchableProp.index())); mdMapping.setTermVector(AnnotationsBindingUtils.convert(searchableProp.termVector())); mdMapping.setOmitNorms(AnnotationsBindingUtils.convert(searchableProp.omitNorms())); mdMapping.setOmitTf(AnnotationsBindingUtils.convert(searchableProp.omitTf())); mdMapping.setReverse(AnnotationsBindingUtils.convert(searchableProp.reverse())); handleFormat(mdMapping, name, searchableProp.format()); mdMapping.setInternal(false); if (StringUtils.hasLength(searchableProp.analyzer())) { mdMapping.setAnalyzer(searchableProp.analyzer()); } else { mdMapping.setAnalyzer(classMapping.getAnalyzer()); } if (StringUtils.hasLength(searchableProp.nullValue())) { mdMapping.setNullValue(searchableProp.nullValue()); } mdMapping.setExcludeFromAll(AnnotationsBindingUtils.convert(searchableProp.excludeFromAll())); classPropertyMapping.addMapping(mdMapping); } if (metaData != null) { bindMetaData(metaData, classPropertyMapping, clazz, type); } if (metaDatas != null) { for (SearchableMetaData searchableMetaData : metaDatas.value()) { bindMetaData(searchableMetaData, classPropertyMapping, clazz, type); } } } private void bindMetaData(SearchableMetaData searchableMetaData, ClassPropertyMapping classPropertyMapping, Class<?> clazz, Type type) { ClassPropertyMetaDataMapping mdMapping = new ClassPropertyMetaDataMapping(); String name = searchableMetaData.name(); mdMapping.setName(valueLookup.lookupMetaDataName(name)); mdMapping.setPath(new StaticPropertyPath(mdMapping.getName())); if (searchableMetaData.boost() == 1.0f) { mdMapping.setBoost(classPropertyMapping.getBoost()); } else { mdMapping.setBoost(searchableMetaData.boost()); } bindConverter(mdMapping, searchableMetaData.converter(), clazz, type); bindSpellCheck(mdMapping, searchableMetaData.spellCheck()); mdMapping.setAccessor(classPropertyMapping.getAccessor()); mdMapping.setPropertyName(classPropertyMapping.getPropertyName()); mdMapping.setStore(AnnotationsBindingUtils.convert(searchableMetaData.store())); mdMapping.setIndex(AnnotationsBindingUtils.convert(searchableMetaData.index())); mdMapping.setTermVector(AnnotationsBindingUtils.convert(searchableMetaData.termVector())); mdMapping.setOmitNorms(AnnotationsBindingUtils.convert(searchableMetaData.omitNorms())); mdMapping.setOmitTf(AnnotationsBindingUtils.convert(searchableMetaData.omitTf())); mdMapping.setReverse(AnnotationsBindingUtils.convert(searchableMetaData.reverse())); handleFormat(mdMapping, name, searchableMetaData.format()); mdMapping.setInternal(false); if (StringUtils.hasLength(searchableMetaData.analyzer())) { mdMapping.setAnalyzer(searchableMetaData.analyzer()); } else { mdMapping.setAnalyzer(classMapping.getAnalyzer()); } if (StringUtils.hasLength(searchableMetaData.nullValue())) { mdMapping.setNullValue(searchableMetaData.nullValue()); } mdMapping.setExcludeFromAll(AnnotationsBindingUtils.convert(searchableMetaData.excludeFromAll())); classPropertyMapping.addMapping(mdMapping); } private void bindDynamicMetaData(SearchableDynamicMetaData searchableMetaData) { DynamicMetaDataMapping mdMapping = new DynamicMetaDataMapping(); String name = searchableMetaData.name(); mdMapping.setName(valueLookup.lookupMetaDataName(name)); mdMapping.setPath(new StaticPropertyPath(mdMapping.getName())); mdMapping.setBoost(searchableMetaData.boost()); mdMapping.setOverrideByName(searchableMetaData.override()); mdMapping.setConverterName(searchableMetaData.converter()); mdMapping.setExpression(searchableMetaData.expression()); if (StringUtils.hasLength(searchableMetaData.format())) { mdMapping.setFormat(searchableMetaData.format()); } mdMapping.setType(searchableMetaData.type()); mdMapping.setStore(AnnotationsBindingUtils.convert(searchableMetaData.store())); mdMapping.setIndex(AnnotationsBindingUtils.convert(searchableMetaData.index())); mdMapping.setTermVector(AnnotationsBindingUtils.convert(searchableMetaData.termVector())); mdMapping.setReverse(AnnotationsBindingUtils.convert(searchableMetaData.reverse())); mdMapping.setInternal(false); bindSpellCheck(mdMapping, searchableMetaData.spellCheck()); if (StringUtils.hasLength(searchableMetaData.analyzer())) { mdMapping.setAnalyzer(searchableMetaData.analyzer()); } else { mdMapping.setAnalyzer(classMapping.getAnalyzer()); } if (StringUtils.hasLength(searchableMetaData.nullValue())) { mdMapping.setNullValue(searchableMetaData.nullValue()); } mdMapping.setExcludeFromAll(AnnotationsBindingUtils.convert(searchableMetaData.excludeFromAll())); classMapping.addMapping(mdMapping); } private void bindConstantMetaData(SearchableConstant searchableConstant) { ConstantMetaDataMapping constantMapping = new ConstantMetaDataMapping(); constantMapping.setName(valueLookup.lookupMetaDataName(searchableConstant.name())); constantMapping.setBoost(searchableConstant.boost()); constantMapping.setStore(AnnotationsBindingUtils.convert(searchableConstant.store())); constantMapping.setIndex(AnnotationsBindingUtils.convert(searchableConstant.index())); constantMapping.setTermVector(AnnotationsBindingUtils.convert(searchableConstant.termVector())); constantMapping.setOmitNorms(AnnotationsBindingUtils.convert(searchableConstant.omitNorms())); constantMapping.setOmitTf(AnnotationsBindingUtils.convert(searchableConstant.omitTf())); if (StringUtils.hasLength(searchableConstant.analyzer())) { constantMapping.setAnalyzer(searchableConstant.analyzer()); } else { constantMapping.setAnalyzer(classMapping.getAnalyzer()); } constantMapping.setExcludeFromAll(AnnotationsBindingUtils.convert(searchableConstant.excludeFromAll())); constantMapping.setOverrideByName(searchableConstant.override()); for (String value : searchableConstant.values()) { constantMapping.addMetaDataValue(valueLookup.lookupMetaDataValue(value)); } bindSpellCheck(constantMapping, searchableConstant.spellCheck()); classMapping.addMapping(constantMapping); } private void bindConverter(InternalMapping mapping, String converterName) { bindConverter(mapping, converterName, null, null); } private void bindSpellCheck(InternalResourcePropertyMapping mapping, org.compass.annotations.SpellCheck spellCheck) { if (spellCheck == org.compass.annotations.SpellCheck.EXCLUDE) { mapping.setSpellCheck(SpellCheck.EXCLUDE); } else if (spellCheck == org.compass.annotations.SpellCheck.INCLUDE) { mapping.setSpellCheck(SpellCheck.INCLUDE); } else if (spellCheck == org.compass.annotations.SpellCheck.NA) { mapping.setSpellCheck(SpellCheck.NA); } } private void bindConverter(InternalMapping mapping, String converterName, Class<?> clazz, Type type) { if (StringUtils.hasLength(converterName)) { mapping.setConverterName(converterName); return; } if (clazz == null) { return; } mapping.setConverter(getConverter(clazz, type)); } public Converter getConverter(Class<?> clazz, Type type) { Class<?> actualClass = AnnotationsBindingUtils.getCollectionParameterClass(clazz, type); if (actualClass == null) { actualClass = clazz; } SearchableClassConverter searchableClassConverter = actualClass.getAnnotation(SearchableClassConverter.class); if (searchableClassConverter == null) { return null; } Object objConverter; try { objConverter = searchableClassConverter.value().newInstance(); } catch (Exception e) { throw new MappingException("Failed to create converter [" + searchableClassConverter.value().getName() + "]", e); } if (!(objConverter instanceof Converter)) { throw new MappingException("[" + searchableClassConverter + "] does not implement Converter interface"); } Converter converter = (Converter) objConverter; if (searchableClassConverter.settings().length > 0 && !(converter instanceof CompassConfigurable)) { throw new MappingException("[" + searchableClassConverter + "] does not implement CompassConfigurable" + " interface, but has settings set, please implement it so settings can be injected"); } if (converter instanceof CompassConfigurable) { CompassSettings settings = new CompassSettings(); for (int i = 0; i < searchableClassConverter.settings().length; i++) { SearchSetting setting = searchableClassConverter.settings()[i]; settings.setSetting(setting.name(), setting.value()); } ((CompassConfigurable) converter).configure(settings); } return converter; } private void bindObjectMapping(InternalObjectMapping objectMapping, String actualAccessor, String name, String annotationAccessor, Class<?> searchableClass) { if (!StringUtils.hasLength(annotationAccessor)) { objectMapping.setAccessor(actualAccessor); } else { objectMapping.setAccessor(annotationAccessor); } objectMapping.setName(name); objectMapping.setPropertyName(name); // set the defined in alias for multiple ref aliases // note, with annotation, we might not have @Searchable defined on // the class, so we are using the FQN class name instead if (searchableClass.isAnnotationPresent(Searchable.class)) { Searchable searchable = searchableClass.getAnnotation(Searchable.class); objectMapping.setDefinedInAlias(getAliasFromSearchableClass(searchableClass, searchable)); } else { objectMapping.setDefinedInAlias(ClassUtils.getShortName(searchableClass)); } } /** * Returns a string array of aliases from a comma separated string */ private String[] getAliases(String commaSeparatedAliases) { ArrayList<String> aliases = new ArrayList<String>(); StringTokenizer st = new StringTokenizer(commaSeparatedAliases, ","); while (st.hasMoreTokens()) { String extendedAlias = st.nextToken().trim(); Alias alias = valueLookup.lookupAlias(extendedAlias); if (alias == null) { aliases.add(extendedAlias); } else { aliases.add(alias.getName()); } } return aliases.toArray(new String[aliases.size()]); } private void handleFormat(ClassPropertyMetaDataMapping mdMapping, String name, String format) { if (!StringUtils.hasLength(format)) { return; } if (mdMapping.getConverter() == null) { if (format == null) { format = valueLookup.lookupMetaDataFormat(name); } if (format != null) { mdMapping.setConverter(new FormatDelegateConverter(format)); } } } }