/*
* ModeShape (http://www.modeshape.org)
*
* 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.modeshape.jcr.index.local;
import java.util.Comparator;
import org.mapdb.BTreeKeySerializer;
import org.mapdb.DB;
import org.mapdb.Serializer;
import org.modeshape.common.collection.Problems;
import org.modeshape.common.collection.SimpleProblems;
import org.modeshape.jcr.ExecutionContext;
import org.modeshape.jcr.JcrI18n;
import org.modeshape.jcr.NodeTypes;
import org.modeshape.jcr.NodeTypes.Supplier;
import org.modeshape.jcr.api.index.IndexDefinition;
import org.modeshape.jcr.cache.change.ChangeSetAdapter.NodeTypePredicate;
import org.modeshape.jcr.index.local.IndexValues.Converter;
import org.modeshape.jcr.index.local.MapDB.Serializers;
import org.modeshape.jcr.spi.index.provider.ManagedIndexBuilder;
import org.modeshape.jcr.spi.index.provider.ProvidedIndex;
import org.modeshape.jcr.value.PropertyType;
import org.modeshape.jcr.value.ValueComparators;
import org.modeshape.jcr.value.ValueFactory;
/**
* A builder for {@link LocalIndex local indexes}.
*
* @author Randall Hauch (rhauch@redhat.com)
* @author Horia Chiorean (hchiorea@redhat.com)
* @param <T> the type of value that is indexed
*/
public abstract class LocalIndexBuilder<T> extends ManagedIndexBuilder {
/**
* Create a builder for the supplied index definition.
*
* @param context the execution context in which the index should operate; may not be null
* @param defn the index definition; may not be null
* @param nodeTypesSupplier the supplier of the {@link NodeTypes} instance; may not be null
* @param workspaceName the name of the workspace for which to build the index; may not be null
* @param matcher the node type matcher used to determine which nodes should be included in the index; may not be null
* @param db the MapDB DB instance; may not be null
* @return the index builder; never null
*/
public static <T> LocalIndexBuilder<T> create( ExecutionContext context,
IndexDefinition defn,
Supplier nodeTypesSupplier,
String workspaceName,
NodeTypePredicate matcher,
DB db) {
SimpleProblems problems = new SimpleProblems();
validate(defn, problems);
if (problems.hasErrors()) {
throw new LocalIndexException(problems.toString());
}
PropertyType actualPropertyType = determineActualPropertyType(defn.getColumnDefinition(0));
return new SingleColumnIndexBuilder<>(context, defn, nodeTypesSupplier,workspaceName, matcher, actualPropertyType, db);
}
protected final Serializers serializers;
protected LocalIndexBuilder( ExecutionContext context,
IndexDefinition defn,
Supplier nodeTypesSupplier,
String workspaceName,
NodeTypePredicate matcher ) {
super(context, defn, workspaceName, nodeTypesSupplier, matcher);
this.serializers = MapDB.serializers(this.context.getValueFactories());
}
/**
* Validate whether the index definition is acceptable for this provider.
*
* @param defn the definition to validate; may not be {@code null}
* @param problems the component to record any problems, errors, or warnings; may not be null
*/
protected static void validate( IndexDefinition defn, Problems problems ) {
if (!defn.hasSingleColumn()) {
problems.addError(JcrI18n.localIndexProviderDoesNotSupportMultiColumnIndexes, defn.getName(), defn.getProviderName());
}
switch (defn.getKind()) {
case TEXT:
// This is not valid ...
problems.addError(JcrI18n.localIndexProviderDoesNotSupportTextIndexes, defn.getName(),defn.getProviderName());
}
}
protected final String indexName() {
return defn.getName();
}
protected abstract Serializer<T> getSerializer();
protected abstract Comparator<T> getComparator();
protected static class SingleColumnIndexBuilder<T> extends LocalIndexBuilder<T> {
private final PropertyType type;
private final Serializer<T> serializer;
private final BTreeKeySerializer<T> btreeKeySerializer;
private final Comparator<T> comparator;
private final BTreeKeySerializer<String> stringBtreeSerializer;
private final Comparator<String> stringComparator;
private final Class<T> clazz;
private final Converter<T> converter;
private final Converter<String> stringConverter;
private final ValueFactory<T> factory;
private final ValueFactory<String> stringFactory;
private final DB db;
@SuppressWarnings( "unchecked" )
protected SingleColumnIndexBuilder( ExecutionContext context,
IndexDefinition defn,
Supplier nodeTypesSupplier,
String workspaceName,
NodeTypePredicate matcher,
PropertyType actualPropertyType,
DB db ) {
super(context, defn, nodeTypesSupplier, workspaceName, matcher);
assert defn.hasSingleColumn();
type = actualPropertyType;
clazz = (Class<T>)type.getValueClass();
serializer = (Serializer<T>)serializers.serializerFor(clazz);
comparator = (Comparator<T>)type.getComparator();
btreeKeySerializer = (BTreeKeySerializer<T>)serializers.bTreeKeySerializerFor(clazz, comparator, false);
factory = (ValueFactory<T>)this.context.getValueFactories().getValueFactory(type);
converter = IndexValues.converter(factory);
stringComparator = ValueComparators.STRING_COMPARATOR;
stringFactory = this.context.getValueFactories().getStringFactory();
stringBtreeSerializer = (BTreeKeySerializer<String>)serializers.bTreeKeySerializerFor(String.class, stringComparator,
false);
stringConverter = IndexValues.converter(stringFactory);
this.db = db;
}
@Override
protected Serializer<T> getSerializer() {
return serializer;
}
@Override
protected Comparator<T> getComparator() {
return comparator;
}
protected PropertyType getColumnType() {
return type;
}
protected Converter<T> getConverter() {
return converter;
}
protected BTreeKeySerializer<T> getBTreeKeySerializer() {
return btreeKeySerializer;
}
@Override
protected ProvidedIndex<?> buildMultiValueIndex( ExecutionContext context, IndexDefinition defn, String workspaceName,
Supplier nodeTypesSupplier,
NodeTypePredicate matcher ) {
return LocalDuplicateIndex.create(indexName(), workspaceName, db, getConverter(), getSerializer(), getComparator());
}
@Override
protected ProvidedIndex<?> buildUniqueValueIndex( ExecutionContext context, IndexDefinition defn, String workspaceName,
Supplier nodeTypesSupplier,
NodeTypePredicate matcher ) {
return LocalUniqueIndex.create(indexName(), workspaceName, db, getConverter(), getBTreeKeySerializer(), getSerializer());
}
@Override
protected ProvidedIndex<?> buildEnumeratedIndex( ExecutionContext context, IndexDefinition defn, String workspaceName,
Supplier nodeTypesSupplier,
NodeTypePredicate matcher ) {
return LocalEnumeratedIndex.create(defn.getName(), workspaceName, db, stringConverter, stringBtreeSerializer);
}
@Override
protected ProvidedIndex<?> buildTextIndex( ExecutionContext context, IndexDefinition defn, String workspaceName,
Supplier nodeTypesSupplier,
NodeTypePredicate matcher ) {
throw new UnsupportedOperationException("should not ever see this because validation should prevent such indexes from being used");
}
@Override
protected ProvidedIndex<?> buildNodeTypeIndex( ExecutionContext context, IndexDefinition defn, String workspaceName,
Supplier nodeTypesSupplier,
NodeTypePredicate matcher ) {
return LocalEnumeratedIndex.create(defn.getName(), workspaceName, db, stringConverter, stringBtreeSerializer);
}
}
}