blob: 43ab008ecac0084e073bcbd1761daf2503eb1d4e [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.core.partition.impl.btree;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
/**
* A Cursor of Cursors performing a union on all underlying Cursors resulting
* in the disjunction of expressions represented by the constituant child
* Cursors. This cursor prefetches underlying Cursor values so that it can
* comply with the defined Cursor semantics.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$
*/
public class DisjunctionEnumeration implements NamingEnumeration<IndexRecord>
{
/** The underlying child enumerations */
private final NamingEnumeration<IndexRecord>[] children;
/** LUT used to avoid returning duplicates */
private final Map<Object, Object> candidates = new HashMap<Object, Object>();
/** Index of current cursor used */
private int index = 0;
/** Candidate to return */
private final IndexRecord candidate = new IndexRecord();
/** Prefetched record returned */
private final IndexRecord prefetched = new IndexRecord();
/** Used to determine if this enumeration has been exhausted */
private boolean hasMore = true;
// ------------------------------------------------------------------------
// C O N S T R U C T O R S
// ------------------------------------------------------------------------
/**
* Creates a DisjunctionEnumeration over a set of child NamingEnumerations.
* The returned result is the union of all underlying NamingEnumerations
* without duplicates.
*
* @param children array of child NamingInstances
* @throws NamingException if something goes wrong
*/
public DisjunctionEnumeration( NamingEnumeration<IndexRecord>[] children ) throws NamingException
{
this.children = children;
// Close this cursor if their are no children.
if ( children.length <= 0 )
{
hasMore = false;
return;
}
// Advance to the first cursor that has a candidate for us.
while ( !children[index].hasMore() )
{
index++;
// Close and return if we exhaust the cursors without finding a
// valid candidate to return.
if ( index >= children.length )
{
close();
return;
}
}
// Grab the next candidate and add it's id to the LUT/hash of candidates
IndexRecord rec = children[index].next();
prefetched.copy( rec );
candidates.put( rec.getEntryId(), rec.getEntryId() );
}
// ------------------------------------------------------------------------
// java.util.Enumeration Implementation Methods
// ------------------------------------------------------------------------
/**
* @see java.util.Enumeration#nextElement()
*/
public IndexRecord nextElement()
{
try
{
return next();
}
catch ( NamingException e )
{
throw new NoSuchElementException();
}
}
/**
* @see java.util.Enumeration#hasMoreElements()
*/
public boolean hasMoreElements()
{
return hasMore();
}
// ------------------------------------------------------------------------
// NamingEnumeration Method Implementations
// ------------------------------------------------------------------------
/**
* Advances this Cursor one position. Duplicates are not returned so if
* underlying cursors keep returning duplicates the child cursors will be
* advanced until a unique candidate is found or all child cursors are
* exhausted.
*
* @return a candidate element
* @throws NamingException if an error occurs
*/
public IndexRecord next() throws NamingException
{
// Store the last prefetched candidate to return in candidate
candidate.copy( prefetched );
do
{
// Advance to a Cursor that has the next valid candidate for us.
while ( !children[index].hasMore() )
{
index++;
/* Close and return existing prefetched candidate if we
* have exhausted the underlying Cursors without finding a
* valid candidate to return.
*/
if ( index >= children.length )
{
close();
return candidate;
}
}
// Grab next candidate!
IndexRecord rec = children[index].next();
prefetched.copy( rec );
// Break through do/while if the candidate is seen for the first
// time, meaning we have not returned it already.
}
while ( candidates.containsKey( prefetched.getEntryId() ) );
// Add candidate to LUT of encountered candidates.
candidates.put( candidate.getEntryId(), candidate.getEntryId() );
// Return the original value saved before overwriting prefetched
return candidate;
}
/**
* Tests if a prefetched value exists and a call to advance will hence
* succeed.
*
* @return true if a call to advance will succeed false otherwise.
*/
public boolean hasMore()
{
return hasMore;
}
/**
* Closes all the underlying Cursors and not fail fast. All enumerations
* will have close attempts made on them.
*
* @throws NamingException if we cannot close all enumerations
*/
public void close() throws NamingException
{
Throwable throwable = null;
hasMore = false;
for ( int ii = 0; ii < children.length; ii++ )
{
try
{
// Close all children but don't fail fast meaning don't stop
// closing all children if one fails to close for some reason.
children[ii].close();
}
catch ( Throwable t )
{
throwable = t;
}
}
if ( null != throwable && throwable instanceof NamingException )
{
throw ( NamingException ) throwable;
}
else if ( null != throwable )
{
NamingException ne = new NamingException();
ne.setRootCause( throwable );
throw ne;
}
}
}