/*
* 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));
}
}
}
}