blob: a12fb1b0dafafb9b00ba3a51f85759b31cc1637d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.directory.server.xdbm.search.impl;
import org.apache.directory.server.core.cursor.InvalidCursorPositionException;
import org.apache.directory.server.core.entry.ServerEntry;
import org.apache.directory.server.xdbm.*;
/**
* A Cursor traversing candidates matching a Substring assertion expression.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$
*/
public class SubstringCursor extends AbstractIndexCursor<String, ServerEntry>
{
private static final String UNSUPPORTED_MSG =
"SubstringCursors may not be ordered and do not support positioning by element.";
private final boolean hasIndex;
private final IndexCursor<String,ServerEntry> wrapped;
private final SubstringEvaluator evaluator;
private final ForwardIndexEntry<String,ServerEntry> indexEntry =
new ForwardIndexEntry<String,ServerEntry>();
private boolean available = false;
public SubstringCursor( Store<ServerEntry> db,
final SubstringEvaluator substringEvaluator ) throws Exception
{
evaluator = substringEvaluator;
hasIndex = db.hasUserIndexOn( evaluator.getExpression().getAttribute() );
if ( hasIndex )
{
//noinspection unchecked
wrapped = ( ( Index<String,ServerEntry> ) db.getUserIndex( evaluator.getExpression().getAttribute() ) )
.forwardCursor();
}
else
{
/*
* There is no index on the attribute here. We have no choice but
* to perform a full table scan but need to leverage an index for the
* wrapped Cursor. We know that all entries are listed under
* the ndn index and so this will enumerate over all entries. The
* substringEvaluator is used in an assertion to constrain the
* result set to only those entries matching the pattern. The
* substringEvaluator handles all the details of normalization and
* knows to use it, when it itself detects the lack of an index on
* the node's attribute.
*/
wrapped = db.getNdnIndex().forwardCursor();
}
}
public boolean available()
{
return available;
}
public void beforeValue( Long id, String value ) throws Exception
{
throw new UnsupportedOperationException( UNSUPPORTED_MSG );
}
public void afterValue( Long id, String value ) throws Exception
{
throw new UnsupportedOperationException( UNSUPPORTED_MSG );
}
public void before( IndexEntry<String, ServerEntry> element ) throws Exception
{
throw new UnsupportedOperationException( UNSUPPORTED_MSG );
}
public void after( IndexEntry<String, ServerEntry> element ) throws Exception
{
throw new UnsupportedOperationException( UNSUPPORTED_MSG );
}
public void beforeFirst() throws Exception
{
if ( evaluator.getExpression().getInitial() != null && hasIndex )
{
ForwardIndexEntry<String,ServerEntry> indexEntry = new ForwardIndexEntry<String,ServerEntry>();
indexEntry.setValue( evaluator.getExpression().getInitial() );
wrapped.before( indexEntry );
}
else
{
wrapped.beforeFirst();
}
clear();
}
private void clear()
{
available = false;
indexEntry.setObject( null );
indexEntry.setId( null );
indexEntry.setValue( null );
}
public void afterLast() throws Exception
{
// to keep the cursor always *after* the last matched tuple
// This fixes an issue if the last matched tuple is also the last record present in the
// index. In this case the wrapped cursor is positioning on the last tuple instead of positioning after that
wrapped.afterLast();
clear();
}
public boolean first() throws Exception
{
beforeFirst();
return next();
}
private boolean evaluateCandidate( IndexEntry<String,ServerEntry> indexEntry ) throws Exception
{
if ( hasIndex )
{
return evaluator.getPattern().matcher( indexEntry.getValue() ).matches();
}
else
{
return evaluator.evaluate( indexEntry );
}
}
public boolean last() throws Exception
{
afterLast();
return previous();
}
public boolean previous() throws Exception
{
while ( wrapped.previous() )
{
IndexEntry<String,ServerEntry> entry = wrapped.get();
if ( evaluateCandidate( entry ) )
{
available = true;
this.indexEntry.setId( entry.getId() );
this.indexEntry.setValue( entry.getValue() );
this.indexEntry.setObject( entry.getObject() );
return true;
}
}
clear();
return false;
}
public boolean next() throws Exception
{
while ( wrapped.next() )
{
IndexEntry<String,ServerEntry> entry = wrapped.get();
if ( evaluateCandidate( entry ) )
{
available = true;
this.indexEntry.setId( entry.getId() );
this.indexEntry.setValue( entry.getValue() );
this.indexEntry.setObject( entry.getObject() );
return true;
}
}
clear();
return false;
}
public IndexEntry<String, ServerEntry> get() throws Exception
{
if ( available )
{
return indexEntry;
}
throw new InvalidCursorPositionException( "Cursor has yet to be positioned." );
}
public boolean isElementReused()
{
return wrapped.isElementReused();
}
public void close() throws Exception
{
super.close();
wrapped.close();
clear();
}
}