/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.collections.containers; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import xxl.core.cursors.AbstractCursor; import xxl.core.cursors.Cursor; import xxl.core.cursors.Cursors; import xxl.core.cursors.filters.Filter; import xxl.core.cursors.mappers.Mapper; import xxl.core.functions.AbstractFunction; import xxl.core.functions.Constant; import xxl.core.functions.Function; import xxl.core.io.converters.FixedSizeConverter; import xxl.core.predicates.AbstractPredicate; /** * An AbstractContainer implements all methods of the Container interface * except the central methods get, isUsed, ids, objectIdConverter, remove, * reserve, size and update which are abstract.<p> * * It can be much more efficient to implement more methods inside a * real container, especially implementation of insert is strongly recommended.<p> * * Subclasses of AbstractContainer are available which manage the data in main * memory (e.g. {@link MapContainer MapContainer}) and external storage. * The later case will be more common. In order to reduce the number of * accesses to external memory, a buffer can be used to keep "interesting" * data in main memory. This aspect has some impact on the Container * class. Many of the methods below offer a parameter <tt>unfix</tt>. If * true, the method automatically has to unfix the object at the end of the * operation, i.e. an underlying buffer is allowed to remove the object * from memory (probably having it written back to external memory before). * Otherwise (<tt>unfix == false</tt>), the object will remain fixed, i.e. * the object can only be removed from memory if the method <tt>unfix()</tt> * has been called explicitly before. When a container uses a buffer, * the method <tt>flush()</tt> has to be implemented. The method writes * back the modified objects from the buffer to the container. * * @see Container */ public abstract class AbstractContainer implements Container { /** * A factory method to create a default Container. Each * implementation of Container should have a function FACTORY_METHOD * that implements two variants of <code>invoke()</code><br> * <ul> * <dl> * <dt><li><code>Object invoke()</code>:</dt> * <dd>returns <code>new Container()</code>.</dd> * <dt><li><code>Object invoke(Object[] internalDataStructure)</code>:</dt> * <dd>returns <code>new Container((<<i>InternalDataStructure></i>)internalDataStructure[0])</code>.</dd> * </dl> * </ul> * This field is set to * <code>{@link MapContainer#FACTORY_METHOD MapContainer.FACTORY_METHOD}</code>. * * @see Function#invoke() * @see Function#invoke(List) */ public static final Function FACTORY_METHOD = MapContainer.FACTORY_METHOD; /** * Sole constructor. (For invocation by subclass constructors, * typically implicit.) */ public AbstractContainer() { } /** * Removes all elements from the Container. After a call of this * method, <tt>size()</tt> will return 0.<br> * Note, that the implementation of this method relies on the remove * operation of the iterator returned by the method <tt>ids()</tt>. */ public void clear () { Cursors.removeAll(ids()); } /** * Closes the Container and releases all sources. For external * containers, this method closes the files immediately. MOREOVER, all * iterators operating on the container can be in illegal states. * Close can be called a second time without any impact. The default * implementation of close is empty (which is OK for a MapContainer).<br> */ public void close () { } /** * Returns true if there is an object stored within the container * having the identifier <tt>id</tt>. * This implementation uses the get method to make the decision. * * @param id identifier of the object. * @return true if the container contains an object for the specified * identifier. */ public boolean contains (Object id) { try { get(id); return true; } catch (NoSuchElementException nsee) { return false; } } /** * Flushes all modified elements from the buffer into the container. * After this call the buffer and the container are synchronized. * The default implementation of flush is empty which is OK for an * unbuffered container. */ public void flush () { } /** * Flushes the object with identifier <tt>id</tt> from the buffer into * the container. The default implementation of flush is empty which is * OK for an unbuffered container. * * @param id identifier of the object that should be written back. */ public void flush (Object id) { } /** * Returns the object associated to the identifier <tt>id</tt>. An * exception is thrown if there is not object stored with this * <tt>id</tt>. If unfix is set to true, the object can be removed * from the underlying buffer. Otherwise (!unfix), the object has * to be kept in the buffer. * * @param id identifier of the object. * @param unfix signals whether the object can be removed from the * underlying buffer. * @return the object associated to the specified identifier. * @throws NoSuchElementException if the desired object is not found. */ public abstract Object get (Object id, boolean unfix) throws NoSuchElementException; /** * Returns the object associated to the identifier <tt>id</tt>. It is * equivalent to get(id, true). * * @param id identifier of the object. * @return the object associated to the specified identifier. * @throws NoSuchElementException if the desired object is not found. */ public Object get (Object id) { return get(id, true); } /** * Returns an iterator referring to the objects of the identifiers * which are delivered by the iterator <tt>ids</tt>s. The parameter * <tt>unfix</tt> specifies whether the object can be removed from the * buffer, see also the discussion of the method <tt>get()</tt>.<br> * The default implementation of this method is lazy, i. e. when the * output iterator should deliver the next element, next is called for * the input iterator. * * @param ids an iterator of identifiers. * @param unfix signals whether the objects of the iterator can be * removed from the underlying buffer. * @return an iterator of objects. * @throws NoSuchElementException if one of the desired objects is not * found. */ public Iterator getAll (Iterator ids, final boolean unfix) { return new Mapper( new AbstractFunction () { public Object invoke (Object id) { return get(id, unfix); } }, ids ); } /** * Returns an iterator pointing to the Object of the given ids. It * behaves like a call of <tt>getAll(ids, true)</tt>. * * @param ids an iterator of object identifiers. * @return an iterator of objects. * @throws NoSuchElementException if one of the desired objects is not * found. */ public Iterator getAll (Iterator ids) { return getAll(ids, true); } /** * Returns an iterator that delivers all the identifiers of * the container that are in use. * * @return an iterator of all identifiers used by this container. */ public abstract Iterator ids (); /** * Inserts a new object into the container and returns the unique * identifier that the container has been associated to the object. * The identifier can be reused again when the object is deleted from * the container. If unfixed, the object can be removed from the * buffer. Otherwise, it has to be kept in the buffer until an * <tt>unfix()</tt> is called.<br> * After an insertion all the iterators operating on the container can * be in an invalid state.<br> * This method also allows an insertion of a null object. In the * application would really like to have such objects in the * container, some methods have to be modified. * This implementation first reserves an id and then updates the id * with the given object. * * @param object is the new object. * @param unfix signals whether the object can be removed from the * underlying buffer. * @return the identifier of the object. */ public Object insert (Object object, boolean unfix) { Object id = reserve(new Constant(object)); update(id, object, unfix); return id; } /** * Inserts a new object into the container and returns the unique * identifier. This methods behaves like * <tt>insert(object, true)</tt>. * * @param object is the new object. * @return the identifier of the object. */ public Object insert (Object object) { return insert(object, true); } /** * Inserts all objects of a given Iterator into the container. It * returns an iterator that contains the identifiers of the new * objects.<br> * Note, that the order of the identifiers corresponds to the order of * their objects. The meaning of unfix is the same as for the method * insert. The default implementation is based on the functional * concept (see class {@link Function Function}) and processes the * input in a lazy fashion. An insertion of an object is performed * when the application program operates on the output iterator! * * @param objects an iterator of objects that should be inserted. * @param unfix signals whether the object can be removed from the * underlying buffer. * @return an iterator containing the identifiers of the objects of * the input iterator. */ public Iterator insertAll (Iterator objects, final boolean unfix) { return new Mapper( new AbstractFunction () { public Object invoke (Object object) { return insert(object, unfix); } }, objects ); } /** * Inserts all objects of a given Iterator into the container. This * method behaves like <tt>insertAll(objects, true)</tt>. * * @param objects an iterator of objects that should be inserted. * @return an iterator containing the identifiers of the objects of * the input iterator. */ public Iterator insertAll (Iterator objects) { return insertAll(objects, true); } /** * Checks whether the <tt>id</tt> has been returned previously by a * call to insert or reserve and hasn't been removed so far. * * @param id the id to be checked. * @return true exactly if the <tt>id</tt> is still in use. */ public abstract boolean isUsed (Object id); /** * Returns a cursor containing all objects of the container. The order * in which the cursor delivers the objects is not specified.<br> * The default implementation filters the iterator that is returned * by the method <tt>ids()</tt> by dropping all ids that doesn't * contain an object. The objects are retrieved from the container * by using the method <tt>get()</tt>. * * @return a cursor containing all objects of the container. */ public Cursor objects () { return new AbstractCursor() { Iterator ids = new Filter(ids(), new AbstractPredicate() { public boolean invoke(Object id) { return AbstractContainer.this.contains(id); } } ); Object id; public boolean hasNextObject() { return ids.hasNext(); } public Object nextObject() { id = ids.next(); return AbstractContainer.this.get(id); } public void remove () throws IllegalStateException { super.remove(); ids.remove(); } public boolean supportsRemove() { return true; } public void reset () { super.reset(); ids = ids(); } public boolean supportsReset() { return true; } public void update (Object object) throws IllegalStateException { super.update(object); AbstractContainer.this.update(id, object); } public boolean supportsUpdate() { return true; } }; } /** * Returns a converter for the ids generated by this container. A * converter transforms an object to its byte representation and vice * versa - also known as serialization in Java.<br> * Since the identifier may have an arbitrary type (which has to be * known in the container), the container has to provide such a method * when the data is not stored in main memory. * * @return a converter for serializing the identifiers of the * container. */ public abstract FixedSizeConverter objectIdConverter (); /** * Returns the size of the ids generated by this container in bytes. * Each id must have the same size. * @return the size in bytes of each id. */ public int getIdSize() { return objectIdConverter().getSerializedSize(); } /** * Removes the object with identifier <tt>id</tt>. An exception is * thrown when an object with an identifier <tt>id</tt> is not in the * container.<br> * After a call of <tt>remove()</tt> all the iterators (and cursors) * can be in an invalid state. * * @param id an identifier of an object. * @throws NoSuchElementException if an object with an identifier * <tt>id</tt> is not in the container. */ public abstract void remove (Object id) throws NoSuchElementException; /** * Removes the objects with the identifiers given by the iterator * <tt>ids</tt>.<br> * The default implementation calls <tt>remove(id)</tt> for each * identifier of <tt>ids</tt>. * * @param ids an iterator containing identifiers of objects. * @throws NoSuchElementException if an object with an identifier * of <tt>ids</tt> is not in the container. */ public void removeAll (Iterator ids) { while (ids.hasNext()) remove(ids.next()); } /** * Reserves an id for subsequent use. The container may or may not * need an object to be able to reserve an id, depending on the * implementation. If so, it will call the parameterless function * provided by the parameter <tt>getObject</tt>. * * @param getObject A parameterless function providing the object for * that an id should be reserved. * @return the reserved id. */ public abstract Object reserve (Function getObject); /** * Returns the number of elements of the container. * * @return the number of elements. */ public abstract int size (); /** * Unfixes the Object with identifier <tt>id</tt>. This method throws * an exception when the identifier <tt>id</tt> isn't used by * the container. After one call of unfix the buffer is allowed to * remove the object (although the objects have been fixed more than * once).<br> * The default implementation only checks whether an object with * identifier <tt>id</tt> is in the buffer. * * @param id identifier of an object that should be unfixed in the * buffer. * @throws NoSuchElementException if the identifier <tt>id</tt> * isn't used by the container. */ public void unfix (Object id) throws NoSuchElementException { if (!isUsed(id)) throw new NoSuchElementException(); } /** * Unfixes the objects with identifiers given by iterator * <tt>ids</tt>. All the objects are unfixed which belong to one of * the identifiers of ids.<br> * The default implementation calls the method <tt>unfix()</tt> for * each identifier of <tt>ids</tt>. * * @param ids an iterator of identifiers. * @throws NoSuchElementException if the identifier <tt>id</tt> * isn't used by the container. */ public void unfixAll (Iterator ids) { while (ids.hasNext()) unfix(ids.next()); } /** * Overwrites an existing (id,*)-element by (id, object). This method * throws an exception if an object with an identifier <tt>id</tt> * does not exist in the container. * * @param id identifier of the element. * @param object the new object that should be associated to * <tt>id</tt>. * @param unfix signals whether the object can be removed from the * underlying buffer. * @throws NoSuchElementException if an object with an identifier * <tt>id</tt> does not exist in the container. */ public abstract void update (Object id, Object object, boolean unfix) throws NoSuchElementException; /** * Overwrites an existing (id,*)-element by (id, object). This method * throws an exception if an object with an identifier <tt>id</tt> * does not exist in the container. The default implementation only * consists of a call of <tt>update(id, object, true)</tt>. * * @param id identifier of the element. * @param object the new object that should be associated to * <tt>id</tt>. * @throws NoSuchElementException if an object with an identifier * <tt>id</tt> does not exist in the container. */ public void update (Object id, Object object) { update(id, object, true); } /** * Overwrites the elements of a container whose identifiers are given * by iterator <tt>ids</tt>. For each identifier of <tt>ids</tt>, this * method calls <tt>update(id, function.invoke(id))</tt>. * * @param ids an iterator of identifiers. * @param function a function that computes the object which should be * associated to an identifier. * @param unfix signals whether the object can be removed from the * underlying buffer. * @throws NoSuchElementException if an object with an identifier * of <tt>ids</tt> does not exist in the container. */ public void updateAll (Iterator ids, final Function function, boolean unfix) { while (ids.hasNext()) { Object id = ids.next(); update(id, function.invoke(id), unfix); } } /** * Overwrites the elements of a container whose identifiers are given * by iterator <tt>ids</tt>. This method is equivalent to * <tt>updateAll(ids, function, true)</tt>. * * @param ids an iterator of identifiers. * @param function a function that computes the object which should be * associated to an identifier. * @throws NoSuchElementException if an object with an identifier * of <tt>ids</tt> does not exist in the container. */ public void updateAll (Iterator ids, Function function) { updateAll(ids, function, true); } /** * Overwrites the elements of a container whose identifiers are given * by iterator <tt>ids</tt>. The objects that have to be associated to * the elements are also given as an iterator. * * @param ids an iterator of identifiers * @param objects an iterator of objects * @param unfix signals whether the object can be removed from the * underlying buffer. * @throws NoSuchElementException if an object with an identifier * of <tt>ids</tt> does not exist in the container. */ public void updateAll (Iterator ids, Iterator objects, boolean unfix) { while (ids.hasNext() && objects.hasNext()) update(ids.next(), objects.next(), unfix); } /** * Overwrites the elements of a container whose identifiers are given * by iterator <tt>ids</tt>. This method is equivalent to * <tt>updateAll(ids, objects, true)</tt>. * * @param ids an iterator of identifiers * @param objects an iterator of objects * @throws NoSuchElementException if an object with an identifier * of <tt>ids</tt> does not exist in the container. */ public void updateAll (Iterator ids, Iterator objects) { updateAll(ids, objects, true); } @Override public Object[] batchInsert(Object[] blocks) { throw new UnsupportedOperationException(); } }