net.sf.mmm.search.engine.impl.lucene.LuceneFieldManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.mmm.search.engine.impl.lucene.LuceneFieldManagerImpl.java

Source

/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.search.engine.impl.lucene;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Date;

import net.sf.mmm.search.api.config.SearchConfiguration;
import net.sf.mmm.search.api.config.SearchConfigurationHolder;
import net.sf.mmm.search.api.config.SearchFieldConfiguration;
import net.sf.mmm.search.api.config.SearchFieldMode;
import net.sf.mmm.search.api.config.SearchFieldType;
import net.sf.mmm.search.api.config.SearchFields;
import net.sf.mmm.util.component.base.AbstractLoggableObject;
import net.sf.mmm.util.date.api.Iso8601Util;
import net.sf.mmm.util.exception.api.IllegalCaseException;
import net.sf.mmm.util.exception.api.NlsClassCastException;
import net.sf.mmm.util.exception.api.NlsIllegalStateException;
import net.sf.mmm.util.exception.api.NlsNullPointerException;
import net.sf.mmm.util.io.api.IoMode;
import net.sf.mmm.util.io.api.RuntimeIoException;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.TermAttribute;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.util.NumericUtils;

/**
 * This class centralizes the behavior of fields according to their {@link SearchFieldConfiguration} for
 * lucene.
 * 
 * @see Field
 * @see NumericField
 * @see Term
 * @see NumericRangeQuery
 * @see TermRangeQuery
 * 
 * @author Joerg Hohwiller (hohwille at users.sourceforge.net)
 * @since 1.0.0
 */
public class LuceneFieldManagerImpl extends AbstractLoggableObject implements LuceneFieldManager {

    /** The {@link Iso8601Util} for date fields. */
    private final Iso8601Util iso8601Util;

    /** @see #getConfigurationHolder() */
    private final SearchConfigurationHolder<? extends SearchConfiguration> configurationHolder;

    /** The {@link Analyzer} */
    private final Analyzer analyzer;

    /** @see #getPrecisionStep() */
    private Integer precisionStep;

    /**
     * The constructor.
     * 
     * @param configurationHolder is the {@link SearchConfigurationHolder}.
     * @param analyzer the {@link Analyzer}.
     * @param iso8601Util is the {@link Iso8601Util} instance used to handle {@link Date} fields.
     */
    public LuceneFieldManagerImpl(SearchConfigurationHolder<? extends SearchConfiguration> configurationHolder,
            Analyzer analyzer, Iso8601Util iso8601Util) {

        super();
        this.configurationHolder = configurationHolder;
        this.analyzer = analyzer;
        this.iso8601Util = iso8601Util;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public SearchConfigurationHolder<? extends SearchConfiguration> getConfigurationHolder() {

        return this.configurationHolder;
    }

    /**
     * This method gets the {@link SearchFields}.
     * 
     * @return the {@link SearchFields}.
     */
    protected SearchFields getSearchFields() {

        return this.configurationHolder.getBean().getFields();
    }

    /**
     * @return the precisionStep
     */
    @Override
    public int getPrecisionStep() {

        Integer result = this.precisionStep;
        if (result == null) {
            int step = NumericUtils.PRECISION_STEP_DEFAULT;
            String precision = this.configurationHolder.getBean().getProperties()
                    .getProperty(PROPERTY_PRECISION_STEP);
            if (precision != null) {
                try {
                    step = Integer.parseInt(precision);
                } catch (NumberFormatException e) {
                    getLogger().warn("Ignoring illegal value of property " + PROPERTY_PRECISION_STEP, e);
                }
            }
            result = Integer.valueOf(step);
            this.precisionStep = result;
        }
        return result.intValue();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("null")
    public Fieldable createField(String field, Object value) {

        NlsNullPointerException.checkNotNull("field", field);
        NlsNullPointerException.checkNotNull("value", value);
        SearchFieldConfiguration fieldConfiguration = getSearchFields().getOrCreateFieldConfiguration(field);
        SearchFieldType fieldType = fieldConfiguration.getType();
        SearchFieldMode fieldMode = fieldConfiguration.getMode();
        Store store;
        if (fieldMode.isRetrievable()) {
            store = Store.YES;
        } else {
            store = Store.NO;
        }
        Fieldable result;
        NumericField numericField = null;
        if ((fieldType != SearchFieldType.TEXT) && ((fieldType != SearchFieldType.STRING))) {
            numericField = new NumericField(field, getPrecisionStep(), store, fieldMode.isSearchable());
        }
        try {
            switch (fieldType) {
            case TEXT:
                if (value instanceof Reader) {
                    if (fieldMode != SearchFieldMode.SEARCHABLE) {
                        throw new NlsIllegalStateException();
                    }
                    result = new Field(field, (Reader) value);
                } else {
                    Index index;
                    if (fieldMode.isSearchable()) {
                        index = Index.ANALYZED;
                    } else {
                        index = Index.NO;
                    }
                    result = new Field(field, (String) value, store, index);
                }
                break;
            case STRING:
                Index index;
                if (fieldMode.isSearchable()) {
                    index = Index.NOT_ANALYZED;
                } else {
                    index = Index.NO;
                }
                result = new Field(field, (String) value, store, index);
                break;
            case INTEGER:
                Integer i = (Integer) value;
                numericField.setIntValue(i.intValue());
                result = numericField;
                break;
            case LONG:
                Long l = (Long) value;
                numericField.setLongValue(l.longValue());
                result = numericField;
                break;
            case FLOAT:
                Float f = (Float) value;
                numericField.setFloatValue(f.floatValue());
                result = numericField;
                break;
            case DOUBLE:
                Double d = (Double) value;
                numericField.setDoubleValue(d.doubleValue());
                result = numericField;
                break;
            case DATE:
                Date date = (Date) value;
                numericField.setLongValue(date.getTime());
                result = numericField;
                break;
            default:
                throw new IllegalCaseException(SearchFieldType.class, fieldType);
            }
        } catch (ClassCastException e) {
            // TODO: Throw exception explaining the SearchFields configuration
            throw new NlsClassCastException(e, value, fieldType.getFieldClass());
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Term createTerm(String field, Object value) {

        NlsNullPointerException.checkNotNull("field", field);
        NlsNullPointerException.checkNotNull("value", value);
        String normalizedValue;
        SearchFieldConfiguration fieldConfiguration = getSearchFields().getOrCreateFieldConfiguration(field);
        SearchFieldType fieldType = fieldConfiguration.getType();
        boolean isString = (value instanceof String);
        try {
            switch (fieldType) {
            case TEXT:
                try {
                    TokenStream tokenStream = this.analyzer.tokenStream(field, new StringReader((String) value));
                    TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
                    if (tokenStream.incrementToken()) {
                        normalizedValue = termAttribute.term();
                    } else {
                        normalizedValue = "";
                    }
                } catch (IOException e) {
                    throw new RuntimeIoException(e, IoMode.READ);
                }
                break;
            case STRING:
                normalizedValue = (String) value;
                break;
            case INTEGER:
                int i;
                if (isString) {
                    i = Integer.parseInt((String) value);
                } else {
                    i = ((Integer) value).intValue();
                }
                normalizedValue = NumericUtils.intToPrefixCoded(i);
                break;
            case LONG:
                long l;
                if (isString) {
                    l = Long.parseLong((String) value);
                } else {
                    l = ((Long) value).longValue();
                }
                normalizedValue = NumericUtils.longToPrefixCoded(l);
                break;
            case FLOAT:
                float f;
                if (isString) {
                    f = Float.parseFloat((String) value);
                } else {
                    f = ((Float) value).floatValue();
                }
                normalizedValue = NumericUtils.floatToPrefixCoded(f);
                break;
            case DOUBLE:
                double d;
                if (isString) {
                    d = Double.parseDouble((String) value);
                } else {
                    d = ((Double) value).doubleValue();
                }
                normalizedValue = NumericUtils.doubleToPrefixCoded(d);
                break;
            case DATE:
                Date date;
                if (isString) {
                    date = this.iso8601Util.parseDate((String) value);
                } else {
                    date = (Date) value;
                }
                normalizedValue = NumericUtils.longToPrefixCoded(date.getTime());
                break;
            default:
                throw new IllegalCaseException(SearchFieldType.class, fieldType);
            }
        } catch (ClassCastException e) {
            throw new NlsClassCastException(e, value, fieldType.getFieldClass());
        }
        return new Term(field, normalizedValue);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Query createPhraseQuery(String field, String value) {

        NlsNullPointerException.checkNotNull("field", field);
        NlsNullPointerException.checkNotNull("value", value);
        SearchFieldConfiguration fieldConfiguration = getSearchFields().getOrCreateFieldConfiguration(field);
        SearchFieldType fieldType = fieldConfiguration.getType();
        Query result;
        if (fieldType == SearchFieldType.TEXT) {
            PhraseQuery phraseQuery = new PhraseQuery();
            result = phraseQuery;
            try {
                TokenStream tokenStream = this.analyzer.tokenStream(field, new StringReader(value));
                TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
                while (tokenStream.incrementToken()) {
                    phraseQuery.add(new Term(field, termAttribute.term()));
                }
            } catch (IOException e) {
                throw new RuntimeIoException(e, IoMode.READ);
            }
        } else {
            result = new TermQuery(createTerm(field, value));
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Query createRangeQuery(String field, String minimum, String maximum, boolean minimumInclusive,
            boolean maximumInclusive) {

        NlsNullPointerException.checkNotNull("field", field);
        NlsNullPointerException.checkNotNull("minimum", minimum);
        NlsNullPointerException.checkNotNull("maximum", maximum);
        SearchFieldConfiguration fieldConfiguration = getSearchFields().getOrCreateFieldConfiguration(field);
        SearchFieldType fieldType = fieldConfiguration.getType();
        Query result;
        switch (fieldType) {
        case TEXT:
            result = new TermRangeQuery(field, minimum, maximum, minimumInclusive, maximumInclusive);
            break;
        case STRING:
            result = new TermRangeQuery(field, minimum, maximum, minimumInclusive, maximumInclusive);
            break;
        case INTEGER:
            result = NumericRangeQuery.newIntRange(field, getPrecisionStep(), Integer.valueOf(minimum),
                    Integer.valueOf(maximum), minimumInclusive, maximumInclusive);
            break;
        case LONG:
            result = NumericRangeQuery.newLongRange(field, getPrecisionStep(), Long.valueOf(minimum),
                    Long.valueOf(maximum), minimumInclusive, maximumInclusive);
            break;
        case FLOAT:
            result = NumericRangeQuery.newFloatRange(field, getPrecisionStep(), Float.valueOf(minimum),
                    Float.valueOf(maximum), minimumInclusive, maximumInclusive);
            break;
        case DOUBLE:
            result = NumericRangeQuery.newDoubleRange(field, getPrecisionStep(), Double.valueOf(minimum),
                    Double.valueOf(maximum), minimumInclusive, maximumInclusive);
            break;
        case DATE:
            Date minDate = this.iso8601Util.parseDate(minimum);
            Date maxDate = this.iso8601Util.parseDate(maximum);
            result = NumericRangeQuery.newLongRange(field, getPrecisionStep(), Long.valueOf(minDate.getTime()),
                    Long.valueOf(maxDate.getTime()), minimumInclusive, maximumInclusive);
            break;
        default:
            throw new IllegalCaseException(SearchFieldType.class, fieldType);
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean refresh() {

        this.precisionStep = null;
        return this.configurationHolder.refresh();
    }

}