/*
* 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.core.lucene;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Fieldable;
import org.compass.core.Property;
import org.compass.core.Resource;
import org.compass.core.converter.mapping.ResourcePropertyConverter;
import org.compass.core.engine.SearchEngineException;
import org.compass.core.engine.SearchEngineFactory;
import org.compass.core.lucene.engine.LuceneSearchEngineFactory;
import org.compass.core.lucene.support.FieldHelper;
import org.compass.core.mapping.ResourceMapping;
import org.compass.core.mapping.ResourcePropertyMapping;
import org.compass.core.spi.AliasedObject;
import org.compass.core.spi.InternalResource;
import org.compass.core.spi.ResourceKey;
import org.compass.core.util.StringUtils;
/**
* @author kimchy
*/
public class LuceneResource implements AliasedObject, InternalResource, Map<String, Property[]> {
private static final long serialVersionUID = 3904681565727306034L;
private ArrayList<Property> properties = new ArrayList<Property>();
private int docNum;
private float boost = 1.0f;
private transient LuceneSearchEngineFactory searchEngineFactory;
private transient ResourceMapping resourceMapping;
private transient ResourceKey resourceKey;
private String alias;
public LuceneResource(String alias, LuceneSearchEngineFactory searchEngineFactory) {
this(alias, new Document(), -1, searchEngineFactory);
}
public LuceneResource(Document document, int docNum, LuceneSearchEngineFactory searchEngineFactory) {
this(null, document, docNum, searchEngineFactory);
}
public LuceneResource(String alias, Document document, int docNum, LuceneSearchEngineFactory searchEngineFactory) {
this.searchEngineFactory = searchEngineFactory;
String aliasProperty = searchEngineFactory.getAliasProperty();
this.docNum = docNum;
if (alias != null) {
this.alias = alias;
Field aliasField = new Field(aliasProperty, alias, Field.Store.YES, Field.Index.NOT_ANALYZED);
aliasField.setOmitNorms(true);
properties.add(new LuceneProperty(aliasField));
} else {
Fieldable aliasField = document.getField(aliasProperty);
if (aliasField != null) {
properties.add(new LuceneProperty(aliasField));
this.alias = aliasField.stringValue();
}
}
verifyResourceMapping();
List fields = document.getFields();
for (Iterator fieldsIt = fields.iterator(); fieldsIt.hasNext();) {
Fieldable field = (Fieldable) fieldsIt.next();
if (field.name().equals(aliasProperty)) {
continue;
}
LuceneProperty lProperty = new LuceneProperty(field);
lProperty.setPropertyMapping(resourceMapping.getResourcePropertyMapping(field.name()));
properties.add(lProperty);
}
}
public LuceneSearchEngineFactory getSearchEngineFactory() {
return this.searchEngineFactory;
}
public void attach(SearchEngineFactory searchEngineFactory) {
this.searchEngineFactory = (LuceneSearchEngineFactory) searchEngineFactory;
this.resourceMapping = searchEngineFactory.getMapping().getRootMappingByAlias(getAlias());
}
public void copy(Resource resource) {
LuceneResource luceneResource = (LuceneResource) resource;
this.docNum = luceneResource.docNum;
this.properties = luceneResource.properties;
this.alias = luceneResource.alias;
if (luceneResource.searchEngineFactory != null) {
this.searchEngineFactory = luceneResource.searchEngineFactory;
}
if (luceneResource.resourceMapping != null) {
this.resourceMapping = luceneResource.resourceMapping;
}
}
public Document getDocument() {
Document document = new Document();
document.setBoost(boost);
for (Property property : properties) {
document.add(((LuceneProperty) property).getField());
}
return document;
}
public ResourceKey getResourceKey() {
if (resourceKey == null) {
resourceKey = new ResourceKey(resourceMapping, this);
}
return resourceKey;
}
public ResourceMapping getResourceMapping() {
return this.resourceMapping;
}
public String getSubIndex() {
return getResourceKey().getSubIndex();
}
public String getValue(String name) {
for (Property property : properties) {
if (property.getName().equals(name) && (!property.isBinary()))
return property.getStringValue();
}
return null;
}
public Object getObject(String name) {
Property prop = getProperty(name);
if (prop == null) {
return null;
}
return prop.getObjectValue();
}
public Object[] getObjects(String name) {
Property[] props = getProperties(name);
Object[] ret = new Object[props.length];
for (int i = 0; i < props.length; i++) {
ret[i] = props[i].getObjectValue();
}
return ret;
}
private final static String[] NO_STRINGS = new String[0];
public String[] getValues(String name) {
List<String> result = new ArrayList<String>();
for (Property property : properties) {
if (property.getName().equals(name) && (!property.isBinary()))
result.add(property.getStringValue());
}
if (result.size() == 0)
return NO_STRINGS;
return result.toArray(new String[result.size()]);
}
public String getAlias() {
return alias;
}
public String getUID() {
return getResourceKey().buildUID();
}
public String getId() {
String[] ids = getIds();
return ids[0];
}
public String[] getIds() {
Property[] idProperties = getIdProperties();
String[] ids = new String[idProperties.length];
for (int i = 0; i < idProperties.length; i++) {
if (idProperties[i] != null) {
ids[i] = idProperties[i].getStringValue();
}
}
return ids;
}
public Property getIdProperty() {
Property[] idProperties = getIdProperties();
return idProperties[0];
}
public Property[] getIdProperties() {
return getResourceKey().getIds();
}
public Resource addProperty(String name, Object value) throws SearchEngineException {
String alias = getAlias();
ResourcePropertyMapping propertyMapping = resourceMapping.getResourcePropertyMapping(name);
if (propertyMapping == null) {
throw new SearchEngineException("No resource property mapping is defined for alias [" + alias
+ "] and resource property [" + name + "]");
}
ResourcePropertyConverter converter = propertyMapping.getResourcePropertyConverter();
if (converter == null) {
converter = (ResourcePropertyConverter) searchEngineFactory.getMapping().
getConverterLookup().lookupConverter(value.getClass());
}
String strValue = converter.toString(value, propertyMapping);
Property property = searchEngineFactory.getResourceFactory().createProperty(strValue, propertyMapping);
property.setBoost(propertyMapping.getBoost());
return addProperty(property);
}
public Resource addProperty(String name, Reader value) throws SearchEngineException {
String alias = getAlias();
ResourcePropertyMapping propertyMapping = resourceMapping.getResourcePropertyMapping(name);
if (propertyMapping == null) {
throw new SearchEngineException("No resource property mapping is defined for alias [" + alias
+ "] and resource property [" + name + "]");
}
Field.TermVector fieldTermVector = FieldHelper.getFieldTermVector(propertyMapping.getTermVector());
Field field = new Field(name, value, fieldTermVector);
LuceneProperty property = new LuceneProperty(field);
property.setBoost(propertyMapping.getBoost());
property.setPropertyMapping(propertyMapping);
return addProperty(property);
}
public Resource addProperty(Property property) {
LuceneProperty lProperty = (LuceneProperty) property;
lProperty.setPropertyMapping(resourceMapping.getResourcePropertyMapping(property.getName()));
properties.add(property);
return this;
}
public Resource setProperty(String name, Object value) throws SearchEngineException {
removeProperties(name);
return addProperty(name, value);
}
public Resource setProperty(String name, Reader value) throws SearchEngineException {
removeProperties(name);
return addProperty(name, value);
}
public Resource setProperty(Property property) {
removeProperties(property.getName());
return addProperty(property);
}
public Resource removeProperty(String name) {
Iterator<Property> it = properties.iterator();
while (it.hasNext()) {
Property property = it.next();
if (property.getName().equals(name)) {
it.remove();
return this;
}
}
return this;
}
public Resource removeProperties(String name) {
Iterator<Property> it = properties.iterator();
while (it.hasNext()) {
Property property = it.next();
if (property.getName().equals(name)) {
it.remove();
}
}
return this;
}
public Property getProperty(String name) {
for (Property property : properties) {
if (property.getName().equals(name)) {
return property;
}
}
return null;
}
public Property[] getProperties(String name) {
List<Property> result = new ArrayList<Property>();
for (int i = 0; i < properties.size(); i++) {
Property property = properties.get(i);
if (property.getName().equals(name)) {
result.add(property);
}
}
if (result.size() == 0)
return new Property[0];
return result.toArray(new Property[result.size()]);
}
public Property[] getProperties() {
return properties.toArray(new Property[properties.size()]);
}
public float getBoost() {
return boost;
}
public Resource setBoost(float boost) {
this.boost = boost;
return this;
}
public void setDocNum(int docNum) {
this.docNum = docNum;
}
/**
* Returns the Lucene document number. If not set (can be in case the
* resource is newly created), than returns -1.
*/
public int getDocNum() {
return this.docNum;
}
public void addUID() {
removeProperties(resourceMapping.getUIDPath());
Property uidProp = searchEngineFactory.getResourceFactory().createProperty(resourceMapping.getUIDPath(),
getResourceKey().buildUID(), Property.Store.YES, Property.Index.NOT_ANALYZED);
uidProp.setOmitNorms(true);
uidProp.setOmitTf(true);
addProperty(uidProp);
}
private void verifyResourceMapping() throws SearchEngineException {
String alias = getAlias();
if (resourceMapping == null) {
if (alias == null) {
throw new SearchEngineException(
"Can't add a resource property based on resource mapping without an alias associated with the resource first");
}
if (!searchEngineFactory.getMapping().hasRootMappingByAlias(alias)) {
throw new SearchEngineException("No mapping is defined for alias [" + alias + "]");
}
resourceMapping = searchEngineFactory.getMapping().getRootMappingByAlias(alias);
}
}
public String toString() {
return "{" + getAlias() + "} " + StringUtils.arrayToCommaDelimitedString(getProperties());
}
// methods from the Map interface
// ------------------------------
public void clear() {
throw new UnsupportedOperationException("Map operations are supported for read operations only");
}
public boolean containsValue(Object value) {
throw new UnsupportedOperationException("Map operations are supported for read operations only");
}
public void putAll(Map<? extends String, ? extends Property[]> t) {
for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Entry) it.next();
put((String) entry.getKey(), (Property[]) entry.getValue());
}
}
public Property[] remove(Object key) {
removeProperties(key.toString());
// TODO should return the old value
return null;
}
public Property[] put(String key, Property[] value) {
removeProperties(key);
for (Property aValue : value) {
addProperty(aValue);
}
// TODO should return the old value
return null;
}
public Set<Map.Entry<String, Property[]>> entrySet() {
Set<String> keySey = keySet();
Set<Entry<String, Property[]>> entrySet = new HashSet<Entry<String, Property[]>>();
for (Iterator it = keySey.iterator(); it.hasNext();) {
final String name = it.next().toString();
final Property[] props = getProperties(name);
entrySet.add(new Map.Entry<String, Property[]>() {
public String getKey() {
return name;
}
public Property[] getValue() {
return props;
}
public Property[] setValue(Property[] value) {
put(name, value);
// TODO should return the old value
return null;
}
});
}
return Collections.unmodifiableSet(entrySet);
}
public Set<String> keySet() {
Set<String> keySet = new HashSet<String>();
for (Property property : properties) {
keySet.add((property).getName());
}
return Collections.unmodifiableSet(keySet);
}
public boolean containsKey(Object key) {
return getProperty(key.toString()) != null;
}
public int size() {
return this.properties.size();
}
public boolean isEmpty() {
return this.properties.isEmpty();
}
public Collection<Property[]> values() {
Set<String> keySet = keySet();
List<Property[]> values = new ArrayList<Property[]>();
for (String name : keySet) {
values.add(getProperties(name));
}
return Collections.unmodifiableList(values);
}
public Property[] get(Object key) {
return getProperties(key.toString());
}
}