/* 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.cursors.identities;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.NoSuchElementException;
import xxl.core.cursors.DecoratorCursor;
import xxl.core.cursors.MetaDataCursor;
/**
* This class decorates a {@link xxl.core.cursors.Cursor cursor} or a
* {@link xxl.core.cursors.MetaDataCursor metadata-cursor} passing by all its
* method calls while writing logging information to a print-stream. It realizes
* the design pattern named <i>Decorator</i>.
*
* <p><b>Note:</b> When the given input iteration only implements the interface
* {@link java.util.Iterator} it is wrapped to a cursor by a call to the static
* method {@link xxl.core.cursors.Cursors#wrap(Iterator) wrap}.</p>
*
* <p>This class is useful for debugging purposes. Such a logging-cursor can be
* included easily into an operator tree in order to log method calls.</p>
*
* @see java.util.Iterator
* @see xxl.core.cursors.Cursor
* @see xxl.core.cursors.SecureDecoratorCursor
*/
public class LoggingMetaDataCursor extends DecoratorCursor implements MetaDataCursor {
/**
* A print-stream to which the logging information will be sent.
*/
protected PrintStream printStream;
/**
* A name that is used to identify this instance of a logging-cursor in
* bulked log file, etc.
*/
protected String name;
/**
* A boolean flag deciding whether the logging information should be sent to
* <tt>printStream</tt> or not.
*/
protected boolean doLogging;
/**
* A boolean flag deciding whether the objects flowing through this operator
* should also be logged.
*/
protected boolean printData;
/**
* Creates a new logging-cursor. If an iterator is given to this constructor
* it is wrapped to a cursor.
*
* @param iterator the iteration to be decorated and logged.
* @param printStream a print-stream to which the logging information will be
* sent.
* @param name a name that is used to identify this instance of a
* logging-cursor in bulked log file, etc.
* @param printData determines whether the objects flowing through this
* operator should also be logged.
*/
public LoggingMetaDataCursor(Iterator iterator, PrintStream printStream, String name, boolean printData) {
super(iterator);
this.printStream = printStream;
this.name = name;
this.doLogging = true;
this.printData = printData;
logEntry("constructed", true);
}
/**
* This method writes the logging information concerning a method call to
* <tt>printStream</tt> if and only if <tt>doLogging</tt> is set to
* <tt>true</tt>.
*
* @param method the name of the logged method.
* @param after determines whether the logging information is sent after the
* execution of the logged method.
*
*/
protected void logEntry(String method, boolean after) {
if (doLogging) {
printStream.print(name);
printStream.print("\tCursor\t");
printStream.print(method);
printStream.print("\t");
printStream.println(after ? "after" : "before");
}
}
/**
* This method writes the logging information concerning the parameters of a
* method call to <tt>printStream</tt> if and only if <tt>doLogging</tt> and
* <tt>printData</tt> are set to <tt>true</tt>.
*
* @param method the name of the logged method.
* @param data a string describing the parameters of the logged method call.
* @param in a boolean flag determining whether the logged parameter is an
* input or an output parameter.
*/
protected void logData(String method, String data, boolean in) {
if (doLogging && printData) {
printStream.print(name);
printStream.print("\tCursor\t");
printStream.print(method);
printStream.print("\t");
printStream.println((in ? "input: " : "output: ") + data);
}
}
/**
* Sets the logging mode.
*
* @param doLogging if <tt>true</tt> logging information is written to the
* specified print-stream.
*/
public void setLoggingMode(boolean doLogging) {
this.doLogging = doLogging;
}
/**
* Opens the cursor, i.e., signals the cursor to reserve resources, open
* files, etc. Before a cursor has been opened calls to methods like
* <tt>next</tt> or <tt>peek</tt> are not guaranteed to yield proper
* results. Therefore <tt>open</tt> must be called before a cursor's data
* can be processed. Multiple calls to <tt>open</tt> do not have any effect,
* i.e., if <tt>open</tt> was called the cursor remains in the state
* <i>opened</i> until its <tt>close</tt> method is called.
*
* <p>Note, that a call to the <tt>open</tt> method of a closed cursor
* usually does not open it again because of the fact that its state
* generally cannot be restored when resources are released respectively
* files are closed.</p>
*/
public void open() {
logEntry("open", false);
super.open();
logEntry("open", true);
}
/**
* Closes the cursor, i.e., signals the cursor to clean up resources, close
* files, etc. When a cursor has been closed calls to methods like
* <tt>next</tt> or <tt>peek</tt> are not guaranteed to yield proper
* results. Multiple calls to <tt>close</tt> do not have any effect, i.e.,
* if <tt>close</tt> was called the cursor remains in the state
* <i>closed</i>.
*
* <p>Note, that a closed cursor usually cannot be opened again because of
* the fact that its state generally cannot be restored when resources are
* released respectively files are closed.</p>
*/
public void close() {
logEntry("close", false);
super.close();
logEntry("close", true);
}
/**
* Returns <tt>true</tt> if the iteration has more elements. (In other
* words, returns <tt>true</tt> if <tt>next</tt> or <tt>peek</tt> would
* return an element rather than throwing an exception.)
*
* <p>This operation is implemented idempotent, i.e., consequent calls to
* <tt>hasNext</tt> do not have any effect.</p>
*
* @return <tt>true</tt> if the cursor has more elements.
* @throws IllegalStateException if the cursor is already closed when this
* method is called.
*/
public boolean hasNext() throws IllegalStateException {
logEntry("hasNext", false);
boolean hasNext = super.hasNext();
logData("hasNext", Boolean.toString(hasNext), false);
logEntry("hasNext", true);
return hasNext;
}
/**
* Returns the next element in the iteration. This element will be
* accessible by some of the cursor's methods, e.g., <tt>update</tt> or
* <tt>remove</tt>, until a call to <tt>next</tt> or <tt>peek</tt> occurs.
* This is calling <tt>next</tt> or <tt>peek</tt> proceeds the iteration and
* therefore its previous element will not be accessible any more.
*
* @return the next element in the iteration.
* @throws IllegalStateException if the cursor is already closed when this
* method is called.
* @throws NoSuchElementException if the iteration has no more elements.
*/
public Object next() throws IllegalStateException, NoSuchElementException {
logEntry("next", false);
Object next = super.next();
logData("next", next.toString(), false);
logEntry("next", true);
return next;
}
/**
* Shows the next element in the iteration without proceeding the iteration
* (optional operation). After calling <tt>peek</tt> the returned element is
* still the cursor's next one such that a call to <tt>next</tt> would be
* the only way to proceed the iteration. But be aware that an
* implementation of this method uses a kind of buffer-strategy, therefore
* it is possible that the returned element will be removed from the
* <i>underlying</i> iteration, e.g., the caller can use an instance of a
* cursor depending on an iterator, so the next element returned by a call
* to <tt>peek</tt> will be removed from the underlying iterator which does
* not support the <tt>peek</tt> operation and therefore the iterator has to
* be wrapped and buffered.
*
* <p>Note, that this operation is optional and might not work for all
* cursors. After calling the <tt>peek</tt> method a call to <tt>next</tt>
* is strongly recommended.</p>
*
* @return the next element in the iteration.
* @throws IllegalStateException if the cursor is already closed when this
* method is called.
* @throws NoSuchElementException iteration has no more elements.
* @throws UnsupportedOperationException if the <tt>peek</tt> operation is
* not supported by the cursor.
*/
public Object peek() throws IllegalStateException, NoSuchElementException, UnsupportedOperationException {
logEntry("peek", false);
Object peek = super.peek();
logData("peek", peek.toString(), false);
logEntry("peek", true);
return peek;
}
/**
* Returns <tt>true</tt> if the <tt>peek</tt> operation is supported by the
* cursor. Otherwise it returns <tt>false</tt>.
*
* @return <tt>true</tt> if the <tt>peek</tt> operation is supported by the
* cursor, otherwise <tt>false</tt>.
*/
public boolean supportsPeek() {
logEntry("supportsPeek", false);
boolean supportsPeek = super.supportsPeek();
logData("supportsPeek", Boolean.toString(supportsPeek), false);
logEntry("supportsPeek", true);
return supportsPeek;
}
/**
* Removes from the underlying data structure the last element returned by
* the cursor (optional operation). This method can be called only once per
* call to <tt>next</tt> or <tt>peek</tt> and removes the element returned
* by this method. Note, that between a call to <tt>next</tt> and
* <tt>remove</tt> the invocation of <tt>peek</tt> or <tt>hasNext</tt> is
* forbidden. The behaviour of a cursor is unspecified if the underlying
* data structure is modified while the iteration is in progress in any way
* other than by calling this method.
*
* <p>Note, that this operation is optional and might not work for all
* cursors.</p>
*
* @throws IllegalStateException if the <tt>next</tt> or <tt>peek</tt> method
* has not yet been called, or the <tt>remove</tt> method has already
* been called after the last call to the <tt>next</tt> or
* <tt>peek</tt> method.
* @throws UnsupportedOperationException if the <tt>remove</tt> operation is
* not supported by the cursor.
*/
public void remove() throws IllegalStateException, UnsupportedOperationException {
logEntry("remove", false);
super.remove();
logEntry("remove", true);
}
/**
* Returns <tt>true</tt> if the <tt>remove</tt> operation is supported by
* the cursor. Otherwise it returns <tt>false</tt>.
*
* @return <tt>true</tt> if the <tt>remove</tt> operation is supported by
* the cursor, otherwise <tt>false</tt>.
*/
public boolean supportsRemove() {
logEntry("supportsRemove", false);
boolean supportsRemove = super.supportsRemove();
logData("supportsRemove", Boolean.toString(supportsRemove), false);
logEntry("supportsRemove", true);
return supportsRemove;
}
/**
* Replaces the last element returned by the cursor in the underlying data
* structure (optional operation). This method can be called only once per
* call to <tt>next</tt> or <tt>peek</tt> and updates the element returned
* by this method. Note, that between a call to <tt>next</tt> and
* <tt>update</tt> the invocation of <tt>peek</tt> or <tt>hasNext</tt> is
* forbidden. The behaviour of a cursor is unspecified if the underlying
* data structure is modified while the iteration is in progress in any way
* other than by calling this method.
*
* <p>Note, that this operation is optional and might not work for all
* cursors.</p>
*
* @param object the object that replaces the last element returned by the
* cursor.
* @throws IllegalStateException if the <tt>next</tt> or <tt>peek</tt> method
* has not yet been called, or the <tt>update</tt> method has already
* been called after the last call to the <tt>next</tt> or
* <tt>peek</tt> method.
* @throws UnsupportedOperationException if the <tt>update</tt> operation is
* not supported by the cursor.
*/
public void update(Object object) throws IllegalStateException, UnsupportedOperationException {
logEntry("update", false);
logData("update", object.toString(), true);
super.update(object);
logEntry("update", true);
}
/**
* Returns <tt>true</tt> if the <tt>update</tt> operation is supported by
* the cursor. Otherwise it returns <tt>false</tt>.
*
* @return <tt>true</tt> if the <tt>update</tt> operation is supported by
* the cursor, otherwise <tt>false</tt>.
*/
public boolean supportsUpdate() {
logEntry("supportsUpdate", false);
boolean supportsUpdate = super.supportsRemove();
logData("supportsUpdate", Boolean.toString(supportsUpdate), false);
logEntry("supportsUpdate", true);
return supportsUpdate;
}
/**
* Resets the cursor to its initial state such that the caller is able to
* traverse the underlying data structure again without constructing a new
* cursor (optional operation). The modifications, removes and updates
* concerning the underlying data structure, are still persistent.
*
* <p>Note, that this operation is optional and might not work for all
* cursors.</p>
*
* @throws UnsupportedOperationException if the <tt>reset</tt> operation is
* not supported by the cursor.
*/
public void reset() throws UnsupportedOperationException {
logEntry("reset", false);
super.reset();
logEntry("reset", true);
}
/**
* Returns <tt>true</tt> if the <tt>reset</tt> operation is supported by
* the cursor. Otherwise it returns <tt>false</tt>.
*
* @return <tt>true</tt> if the <tt>reset</tt> operation is supported by
* the cursor, otherwise <tt>false</tt>.
*/
public boolean supportsReset() {
logEntry("supportsReset", false);
boolean supportsReset = super.supportsReset();
logData("supportsReset", Boolean.toString(supportsReset), false);
logEntry("supportsReset", true);
return supportsReset;
}
/**
* Returns the metadata information for this metadata-cursor. The return
* value of this method can be an arbitrary kind of metadata information,
* e.g., relational metadata information, therefore it is of type
* {@link java.lang.Object}. When using a metadata-cursor, it has to be
* guaranteed, that all elements contained in this metadata-cursor refer to
* the same metadata information. That means, every time <tt>getMetaData</tt>
* is called on a metadata-cursor, it should return exactly the same metadata
* information.
*
* @return an object representing metadata information for this
* metadata-cursor.
*/
public Object getMetaData() {
logEntry("getMetaData", false);
if (!(getDecoree() instanceof MetaDataCursor))
throw new UnsupportedOperationException("no MetaDataCursor specified.");
Object metaData = ((MetaDataCursor)getDecoree()).getMetaData();
logData("getMetaData", metaData.toString(), false);
logEntry("getMetaData", true);
return metaData;
}
}