gt:             while (curr_deleted_node.redo_object != null &&
context: package app.freerouting.datastructures;
  
  import app.freerouting.logger.FRLogger;
  
  import java.util.Collection;
  import java.util.Iterator;
  import java.util.LinkedList;
  import java.util.concurrent.ConcurrentSkipListMap;
  import java.util.concurrent.ConcurrentMap;
  import java.util.Vector;
  
  /**
   * Database of objects, for which Undo and Redo operations are made possible.
   * The algorithm works only for objects containing no references.
   */
  public class UndoableObjects implements java.io.Serializable
  {
  
      /** Creates a new instance of UndoableObjectsList */
      public UndoableObjects()
      {
          stack_level = 0;
          objects = new ConcurrentSkipListMap<Storable, UndoableObjectNode>();
          deleted_objects_stack = new Vector<Collection<UndoableObjectNode>>();
      }
  
      /**
       * Returns an iterator for sequential reading of the object list.
       * Use it together with this.read_object().
       */
      public Iterator<UndoableObjectNode> start_read_object()
      {
          Collection<UndoableObjectNode> object_list = objects.values();
          return object_list.iterator();
      }
  
      /**
       * Reads the next object in this list.
       * Returns null, if the list is exhausted.
       * p_it must be created by start_read_object.
       */
      public UndoableObjects.Storable read_object(Iterator<UndoableObjectNode> p_it)
      {
          while (p_it.hasNext())
          {
              UndoableObjectNode curr_node = p_it.next();
              // skip objects getting alive only by redo
              if (curr_node != null && curr_node.level <= this.stack_level)
              {
                  return (curr_node.object);
              }
          }
          return null;
      }
  
      /**
       * Adds p_object to the UndoableObjectsList.
       */
      public void insert(UndoableObjects.Storable p_object)
      {
          disable_redo();
          UndoableObjectNode curr_undoable_object = new UndoableObjectNode(p_object, stack_level);
          objects.put(p_object, curr_undoable_object);
      }
  
      /**
       * Removes p_object from the top level of the UndoableObjectsList.
       * Returns false, if p_object was not found in the list.
       */
      public boolean delete(UndoableObjects.Storable p_object)
      {
          disable_redo();
          Collection<UndoableObjectNode> curr_delete_list;
          if (deleted_objects_stack.isEmpty())
          {
              // stack_level 0
              curr_delete_list = null;
          }
          else
          {
              curr_delete_list = deleted_objects_stack.lastElement();
          }
          // search p_object in the list
          UndoableObjectNode object_node = objects.get(p_object);
          if (object_node == null)
          {
              return false;
          }
          //if (object_node.object != p_object)
          {   // p_object can be cloned from the object pointed by object_node.object
          	// Since object_node.object has been retrieved via objects.get(p_object)
          	// objects.remove(p_object) would certainly remove the object.
          	// Thus ignore the warning and proceed with the deletion
          	//
              //FRLogger.warn("UndoableObjectList.delete: Object inconsistent");
              //return false;
          }
  
          if (curr_delete_list != null)
          {
              if (object_node.level < this.stack_level)
              {
                  // add curr_ob to the current delete list to make Undo possible.
                  curr_delete_list.add(object_node);
              }
              else if (object_node.undo_object != null)
              {
                  // add curr_ob.undo_object to the current delete list to make Undo possible.
  
                  curr_delete_list.add(object_node.undo_object);
              }
          }
          objects.remove(p_object);
          return true;
      }
  
      /**
       * Makes the current state of the list restorable by Undo.
       */
      public void generate_snapshot()
      {
          disable_redo();
          Collection<UndoableObjectNode> curr_deleted_objects_list = new LinkedList<UndoableObjectNode>();
          deleted_objects_stack.add(curr_deleted_objects_list);
          ++stack_level;
      }
  
      /**
       * Restores the situation before the last snapshot.
       * Outputs the cancelled and the restored objects (if != null) to enable
       * the calling function to take additional actions needed for these objects.
       * Returns false, if no more undo is possible
       */
      public boolean undo(Collection<UndoableObjects.Storable> p_cancelled_objects, Collection<UndoableObjects.Storable> p_restored_objects)
      {
          if (stack_level == 0)
          {
              return false; // no more undo possible
          }
          Iterator<UndoableObjectNode> it = objects.values().iterator();
          while (it.hasNext())
          {
              UndoableObjectNode curr_node = it.next();
              if (curr_node.level == stack_level)
              {
                  if (curr_node.undo_object != null)
                  {
                      // replace the current object by  its previous state.
                      curr_node.undo_object.redo_object = curr_node;
                      objects.put(curr_node.object, curr_node.undo_object);
                      if (p_restored_objects != null)
                      {
                          p_restored_objects.add(curr_node.undo_object.object);
                      }
                  }
                  if (p_cancelled_objects != null)
                  {
                      p_cancelled_objects.add(curr_node.object);
                  }
              }
          }
          // restore the deleted objects
          Collection<UndoableObjectNode> curr_delete_list = deleted_objects_stack.elementAt(stack_level - 1);
          Iterator<UndoableObjectNode> it2 = curr_delete_list.iterator();
          while (it2.hasNext())
          {
              UndoableObjectNode curr_deleted_node = it2.next();
              this.objects.put(curr_deleted_node.object, curr_deleted_node);
              if (p_restored_objects != null)
              {
                  p_restored_objects.add(curr_deleted_node.object);
              }
          }
          --this.stack_level;
          redo_possible = true;
          return true;
      }
  
      /**
       * Restores the situation before the last undo.
       * Outputs the cancelled and the restored objects (if != null)  to enable
       * the calling function to take additional actions needed for these objects.
       * Returns false, if no more redo is possible.
       */
      public boolean redo(Collection<UndoableObjects.Storable> p_cancelled_objects, Collection<UndoableObjects.Storable> p_restored_objects)
      {
          if (this.stack_level >= deleted_objects_stack.size())
          {
              return false; // alredy at the top level
          }
          ++this.stack_level;
          Iterator<UndoableObjectNode> it = objects.values().iterator();
          while (it.hasNext())
          {
              UndoableObjectNode curr_node = it.next();
              if (curr_node.redo_object != null && curr_node.redo_object.level == this.stack_level)
              {
                  // Object was created on a lower level and changed on the currenzt level,
                  // replace the lower level object by the object on the current layer.
                  objects.put(curr_node.object, curr_node.redo_object);
                  if (p_cancelled_objects != null)
                  {
                      p_cancelled_objects.add(curr_node.object);
                  }
                  if (p_restored_objects != null)
                  {
                      p_restored_objects.add(curr_node.redo_object.object);
                  // else the redo_object was deleted on the redo level
                  }
              }
              else if (curr_node.level == this.stack_level)
              {
                  // Object was created on the current level, allow it to be restored.
                  p_restored_objects.add(curr_node.object);
              }
          }
          // Delete the objects, which were deleted on the current level, again.
          Collection<UndoableObjectNode> curr_delete_list = deleted_objects_stack.elementAt(stack_level - 1);
          Iterator<UndoableObjectNode> it2 = curr_delete_list.iterator();
          while (it2.hasNext())
          {
              UndoableObjectNode curr_deleted_node = it2.next();
  