/*******************************************************************************
* Copyright (c) 2012, Directors of the Tyndale STEP Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* Neither the name of the Tyndale House, Cambridge (www.TyndaleHouse.com)
* nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package com.tyndalehouse.step.core.data;
import static com.tyndalehouse.step.core.utils.StringUtils.isNotBlank;
import static com.tyndalehouse.step.core.utils.StringUtils.split;
import static org.apache.lucene.util.Version.LUCENE_30;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.util.Version;
import org.crosswire.common.util.CWProject;
import com.google.inject.Injector;
import com.tyndalehouse.step.core.data.create.PostProcessor;
import com.tyndalehouse.step.core.exceptions.StepInternalException;
import com.tyndalehouse.step.core.utils.IOUtils;
/**
* A configuration of the entity, include the list of fields, etc.
*
* @author chrisburrell
*
*/
public class EntityConfiguration {
private static final String UNABLE_TO_PARSE_CONFIGURATION_FILE = "Unable to parse configuration file";
private static final String ENTITY_FIELDS_PREFIX = "entity.fields.";
private final String name;
private Map<String, FieldConfig> luceneFieldConfiguration;
private Analyzer analyzerInstance;
private PostProcessor postProcessorInstance;
private String path;
private final String entityHome;
private final Injector injector;
/**
* Creates an entity configuration from a file.
*
* @param path the path to where we've stored our entities
* @param entityName the name of the entity
* @param injector the injector
*/
public EntityConfiguration(final String path, final String entityName, final Injector injector) {
this.entityHome = path;
this.name = entityName;
this.injector = injector;
final Properties properties = loadProperties(entityName);
parseProperties(properties);
}
/**
* parses the properties related to an entity configuration
*
* @param properties the set of properties
*/
@SuppressWarnings("unchecked")
private void parseProperties(final Properties properties) {
try {
final String analyzerProperty = properties.getProperty("entity.analyzer");
if (isNotBlank(analyzerProperty)) {
// try the default constructor
final Class<Analyzer> analyzerClass = (Class<Analyzer>) Class.forName(analyzerProperty);
try {
this.analyzerInstance = analyzerClass.newInstance();
} catch (final InstantiationException exception) {
this.analyzerInstance = analyzerClass.getConstructor(Version.class).newInstance(
Version.LUCENE_30);
}
} else {
this.analyzerInstance = new StandardAnalyzer(LUCENE_30);
}
final String processor = properties.getProperty("entity.postProcessor");
if (isNotBlank(processor)) {
this.postProcessorInstance = (PostProcessor) this.injector.getInstance(Class
.forName(processor));
}
} catch (final IllegalAccessException e) {
throw new StepInternalException(UNABLE_TO_PARSE_CONFIGURATION_FILE, e);
} catch (final ClassNotFoundException e) {
throw new StepInternalException(UNABLE_TO_PARSE_CONFIGURATION_FILE, e);
} catch (final InvocationTargetException e) {
throw new StepInternalException(UNABLE_TO_PARSE_CONFIGURATION_FILE, e);
} catch (final InstantiationException e) {
throw new StepInternalException(UNABLE_TO_PARSE_CONFIGURATION_FILE, e);
} catch (final NoSuchMethodException e) {
throw new StepInternalException(UNABLE_TO_PARSE_CONFIGURATION_FILE, e);
}
parseFieldConfigs(properties);
}
/**
* Parses all field configuration
*
* @param properties the set of properties attached to an entity
*/
private void parseFieldConfigs(final Properties properties) {
int initialCapacity = properties.size() - 3;
initialCapacity = initialCapacity > 0 ? initialCapacity : 0;
this.luceneFieldConfiguration = new HashMap<String, FieldConfig>(initialCapacity);
for (final Entry<Object, Object> p : properties.entrySet()) {
if (p.getKey() instanceof String) {
final String key = (String) p.getKey();
if (key.startsWith(ENTITY_FIELDS_PREFIX)) {
parseFieldConfig(key.substring(ENTITY_FIELDS_PREFIX.length()), (String) p.getValue());
}
}
}
}
/**
* parses a single field configuration
*
* @param fieldName the name of the field
* @param value value of the field
*/
private void parseFieldConfig(final String fieldName, final String value) {
final String[] parts = split(value, ",");
final String[] rawFieldMappings = split(parts[0], "\\|");
final FieldConfig fieldConfig;
if(parts.length > 4) {
fieldConfig = new FieldConfig(fieldName, rawFieldMappings, Field.Store.valueOf(parts[1]), Field.Index.valueOf(parts[2]), parts[3], Boolean.parseBoolean(parts[4]));
} else if(parts.length > 3) {
fieldConfig = new FieldConfig(fieldName, rawFieldMappings, Field.Store.valueOf(parts[1]), Field.Index.valueOf(parts[2]), parts[3]);
} else {
fieldConfig = new FieldConfig(fieldName, rawFieldMappings, Field.Store.valueOf(parts[1]), Field.Index.valueOf(parts[2]));
}
this.luceneFieldConfiguration.put(fieldName, fieldConfig);
}
/**
* Loads the properties from file
*
* @param entityName the name of the entity
* @return the set of properties
*/
private Properties loadProperties(final String entityName) {
InputStream resourceAsStream = null;
try {
resourceAsStream = getClass().getResourceAsStream(entityName + ".properties");
final Properties properties = new Properties();
properties.load(resourceAsStream);
return properties;
} catch (final IOException e) {
throw new StepInternalException("Unable to load entity configuration " + entityName, e);
} finally {
IOUtils.closeQuietly(resourceAsStream);
}
}
/**
* @return the location at which the index is stored
*/
public URI getLocation() {
try {
return CWProject.instance().getWriteableProjectSubdir(getPath(), true);
} catch (final IOException e) {
throw new StepInternalException("Unable to create step directory", e);
}
}
/**
* @return the relative path to the entity
*/
private String getPath() {
if (this.path == null) {
this.path = this.entityHome + this.name;
}
return this.path;
}
/**
* @return the name
*/
public String getName() {
return this.name;
}
/**
* @param fieldName the field name
* @return the configuration of this field
*/
public FieldConfig getField(final String fieldName) {
return this.luceneFieldConfiguration.get(fieldName);
}
/**
* @return the luceneFieldConfiguration
*/
public Map<String, FieldConfig> getLuceneFieldConfiguration() {
return this.luceneFieldConfiguration;
}
/**
* @return the analyzerInstance
*/
public Analyzer getAnalyzerInstance() {
return this.analyzerInstance;
}
/**
* @return the postProcessorInstance
*/
public PostProcessor getPostProcessorInstance() {
return this.postProcessorInstance;
}
/**
* @param fieldName the name of the field
* @param fieldValue the value of that field
* @return a {@link Fieldable} which represents these values
*/
public Fieldable getField(final String fieldName, final String fieldValue) {
return this.luceneFieldConfiguration.get(fieldName).getField(fieldValue);
}
}