org.jutil.java.collections
Class RobustVisitor

java.lang.Object
  |
  +--org.jutil.java.collections.RobustVisitor
All Implemented Interfaces:
CollectionOperator
Direct Known Subclasses:
FileSet.FileVisitor

public abstract class RobustVisitor
extends java.lang.Object
implements CollectionOperator

A robust visitor of collections. The code in visit is performed for each element in the visited collection.

Of course, sometimes exceptions can be thrown during a visit. The default behaviour when an exception occurs, is to undo all changes that were made and throw an exception to the caller of the method. This means that the actions that were performed on elements visited before the element that caused the exception, have to be undone. The RobustVisitor class adds support for handling exceptions to the Visitor class.

If an exception occurs while visiting the collection, the changes made to the elements visited before the element that caused the exception, are undone. To accomplish this, a method void unvisit(Object element, Object undoData) is introduced, which takes a visited element and data to undo the changes as arguments. The method must undo the changes made to the given element. The undoData object is the object that is returned by the void visit(Object element) throws Exception method. If no undo data is necessary to undo the changes, null can be returned. The changes are undone in the opposite order as the changes were done. The changes of the visit that caused the exception should be undone by the visit method itself, the other visits are undone using the unvisit method. After all changes are undone, the exception is propagated to the caller of the applyTo method.

The visit and applyTo methods throw Exceptions to keep the class as general as possible. The Exception of the visit method can be strengthened to one specific exception when overwriting the method, but the Exception of the applyTo method can't because that method is made final. That means that in the code you have to catch Exception, which is a disadvantage of using a RobustVisitor. Most methods we didn't think could be overwritten in a useful way are made final. Even if we would allow to overwrite the applyTo method, the only way to strengthen the exception there is to insert a try-catch block and call the super method, or copy the code from the RobustVisitorwhich aren't supported yet in Java.

RobustVisitor doesn't inherit from Visitor because adding an exception to the throws clausule of the visit method would violate LSP (Liskov Substitution Principle) and because the visit methods of both classes have different return types. The last reason also prevents us from doing the opposite, making Visitor inherit from RobustVistor (which would also introduce a useless unvisit method in Visitor).

Undoing changes the traditional way.


 Iterator iter = collection.iterator();
 Vector changed = new Vector();
 Vector undo = new Vector();
 try{
   while(iter.hasNext()) {
     MyClass object = (MyClass) iter.next();
     // action code
     changed.add(object);
 		undo.add(undoData);
   }
 }
 catch(MyException exc) {
   for(int i=changed.size(); i>= 0; i--) {
     // undo action code;
   }
   throw exc;
 }
 

Handling exceptions with the RobustVisitor.


 new RobustVisitor() {
   public Object visit(Object element) throws MyException {
     MyClass object = (MyClass) element;
     // action code;
 		 return undoData;
   }
 	
   public void unvisit(Object element, Object undoData) {
     // undo action code;
   }
 }.applyTo(collection);
 


Inner Class Summary
(package private)  class RobustVisitor.Entry
           
 
Field Summary
static java.lang.String CVS_REVISION
           
 
Constructor Summary
RobustVisitor()
           
 
Method Summary
 java.lang.Object[] applyTo(java.lang.Object[] array)
          Perform the visitation defined in public void visit(Object) on .
 java.util.Collection applyTo(java.util.Collection collection)
          Perform the visitation defined in public void visit(Object) on .
 void applyTo(java.util.Enumeration enumeration)
          Perform the visitation defined in public void visit(Object) on all elements reachable from .
 void applyTo(java.util.Iterator iterator)
          Perform the visitation defined in public void visit(Object) on all elements reachable from .
abstract  void unvisit(java.lang.Object element, java.lang.Object unvisitData)
          This method will be called when the visit method has raised an exception for some element which was visited after .
abstract  java.lang.Object visit(java.lang.Object element)
          The code to be applied to all elements of a collection.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 
Methods inherited from interface org.jutil.java.collections.CollectionOperator
isValidElement
 

Field Detail

CVS_REVISION

public static final java.lang.String CVS_REVISION
Constructor Detail

RobustVisitor

public RobustVisitor()
Method Detail

visit

public abstract java.lang.Object visit(java.lang.Object element)
                                throws java.lang.Exception
The code to be applied to all elements of a collection. If this method throws a Exception, unvisit will be called on all elements that are already visited with the data they returned.

Parameters:
element - The object the code should be applied to.

Specifications:
public behavior
requires isValidElement(element);
ensures (* Data which enables to undo what visit has done in unvisit is returned. *);
signals (Exception) (* Something went wrong while visiting <element>. *);

unvisit

public abstract void unvisit(java.lang.Object element,
                             java.lang.Object unvisitData)
This method will be called when the visit method has raised an exception for some element which was visited after . The implementation should undo whatever visit did on . For that, can be used.

Specifications:
public behavior
requires isValidElement(element);
requires unvisitData == visit(element);
ensures (* the changes on <element> done by visit are undone *);

applyTo

public final java.util.Collection applyTo(java.util.Collection collection)
                                   throws java.lang.Exception,
                                          java.util.ConcurrentModificationException

Perform the visitation defined in public void visit(Object) on . The contents of is not changed.

The collection is returned, so that further operations can be applied to it inline.

Parameters:
collection - The collection to perform this visitation on. This can be null.

Specifications:
public behavior
requires ( \forall Object o; collection.contains(o); isValidElement(o));
ensures \result == collection;
ensures (* for all e in collection: visit(e) *);
signals (ConcurrentModificationException) (* The collection was modified while visiting. *);
signals (Exception) (* Something has gone wrong during the visit *);

applyTo

public final java.lang.Object[] applyTo(java.lang.Object[] array)
                                 throws java.lang.Exception

Perform the visitation defined in public void visit(Object) on . The contents of is not changed.

The array is returned, so that further operations can be applied to it inline.

Parameters:
array - The array to perform this visitation on. This can be null.

Specifications:
ensures \result == array;
ensures (* for all e in collection: visit(e) *);
signals (Exception) (* Something has gone wrong during the visit *);

applyTo

public final void applyTo(java.util.Iterator iterator)
                   throws java.lang.Exception,
                          java.util.ConcurrentModificationException

Perform the visitation defined in public void visit(Object) on all elements reachable from . The contents of the underlying collection is not changed.

Parameters:
iterator - The iterator to perform this visitation on. This can be null.

Specifications:
requires (* The iterator must be at the beginning of its iteration. *);
ensures !iterator.hasNext();
signals (ConcurrentModificationException) (* The collection was modified while visiting. *);
signals (Exception) (* Something has gone wrong during the visit *);

applyTo

public final void applyTo(java.util.Enumeration enumeration)
                   throws java.lang.Exception,
                          java.util.ConcurrentModificationException

Perform the visitation defined in public void visit(Object) on all elements reachable from . The contents of the underlying collection is not changed.

Parameters:
enumeration - The enumeration to perform this visitation on. This can be null.

Specifications:
requires (* The enumeration must be at the beginning of its enumeration. *);
ensures !enumeration.hasMoreElements();
signals (ConcurrentModificationException) (* The collection was modified while visiting. *);
signals (Exception) (* Something has gone wrong during the visit *);