org.jutil.java.collections
Class RobustVisitor

java.lang.Object
  |
  +--org.jutil.java.collections.RobustVisitor
All Implemented Interfaces:
CollectionOperator

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);
 

Version:
$Revision: 1.13 $
Author:
Jan Dockx, Marko van Dooren

Field Summary
static java.lang.String CVS_REVISION
           
 
Constructor Summary
RobustVisitor()
           
 
Method Summary
 java.util.Collection applyTo(java.util.Collection collection)
          public behavior

pre (\forall Object o, collection.contains(o); isValidElement(o));

// The changes are applied to the given collection,
// which is returned afterwards
post \result == collection;
// public void visit(Object) is called for all
// elements of .
post (* for all e in collection: visit(e) *);

signals (ConcurrentModificationException) (* The collection was modified while visiting.
 void applyTo(java.util.Enumeration enumeration)
          pre (* The enumeration must be at the beginning of its enumeration.
 void applyTo(java.util.Iterator iterator)
          pre (* The iterator must be at the beginning of its iteration.
 java.lang.Object[] applyTo(java.lang.Object[] array)
          // The changes are applied to the given array,
// which is returned afterwards
post \result == array;
// public void visit(Object) is called for all
// elements of .
post (* for all e in collection: visit(e) *);

signals (Exception)
(* Something has gone wrong during the visit *);
abstract  void unvisit(java.lang.Object element, java.lang.Object unvisitData)
          public behavior

pre isValidElement(element);

// unvisitData is the data returned by the visit method.
pre unvisitData == visit(element);

post (* the changes on done by visit are undone *);
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)
          public behavior

pre isValidElement(element);

post (* Data which enables to undo what visit has done
in unvisit is returned.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

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
public behavior

pre isValidElement(element);

post (* Data which enables to undo what visit has done
in unvisit is returned. *);

signals (Exception)
(* Something went wrong while visiting . *);
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.

unvisit

public abstract void unvisit(java.lang.Object element,
                             java.lang.Object unvisitData)
public behavior

pre isValidElement(element);

// unvisitData is the data returned by the visit method.
pre unvisitData == visit(element);

post (* the changes on done by visit are undone *);
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.

applyTo

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

pre (\forall Object o, collection.contains(o); isValidElement(o));

// The changes are applied to the given collection,
// which is returned afterwards
post \result == collection;
// public void visit(Object) is called for all
// elements of .
post (* for all e in collection: visit(e) *);

signals (ConcurrentModificationException) (* The collection was modified while visiting. *);
signals (Exception)
(* Something has gone wrong during the visit *);

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.

applyTo

public final java.lang.Object[] applyTo(java.lang.Object[] array)
                                 throws java.lang.Exception
// The changes are applied to the given array,
// which is returned afterwards
post \result == array;
// public void visit(Object) is called for all
// elements of .
post (* for all e in collection: visit(e) *);

signals (Exception)
(* Something has gone wrong during the visit *);

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.

applyTo

public final void applyTo(java.util.Iterator iterator)
                   throws java.lang.Exception,
                          java.util.ConcurrentModificationException
pre (* The iterator must be at the beginning of its iteration. *)

// The iterator will be at the end
post ! iterator.hasNext();

signals (ConcurrentModificationException) (* The collection was modified while visiting. *);
signals (Exception)
(* Something has gone wrong during the visit *);

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.

applyTo

public final void applyTo(java.util.Enumeration enumeration)
                   throws java.lang.Exception,
                          java.util.ConcurrentModificationException
pre (* The enumeration must be at the beginning of its enumeration. *)

// The enumeration will be at the end
post ! enumeration.hasNext();

signals (ConcurrentModificationException) (* The collection was modified while visiting. *);
signals (Exception)
(* Something has gone wrong during the visit *);

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.