/*
 * @(#)CompoundEdit.java	1.20 01/12/03
 *
 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package javax.swing.undo;

import java.util.*;

/**
 * A concrete subclass of AbstractUndoableEdit, used to assemble little
 * UndoableEdits into great big ones.
 *
 * @version 1.20 12/03/01
 * @author Ray Ryan
 */
public class CompoundEdit extends AbstractUndoableEdit {
    /**
     * True if this edit has never received <code>end</code>.
     */
    boolean inProgress;

    /**
     * The collection of <code>UndoableEdit</code>s
     * undone/redone en masse by this <code>CompoundEdit</code>.
     */ 
    protected Vector edits;

    public CompoundEdit() {
	super();
	inProgress = true;
	edits = new Vector();
    }

    /**
     * Sends <code>undoK/code> to all contained
     * <code>UndoableEdits</code> in the reverse of
     * the order in which they were added.
     */
    public void undo() throws CannotUndoException {
	super.undo();
	int i = edits.size();
	while (i-- > 0) {
	    UndoableEdit e = (UndoableEdit)edits.elementAt(i);
	    e.undo();
	}
    }

    /**
     * Sends <code>redo</code> to all contained
     * <code>UndoableEdit</code>s in the order in
     * which they were added.
     */
    public void redo() throws CannotRedoException {
	super.redo();
	Enumeration cursor = edits.elements();
	while (cursor.hasMoreElements()) {
	    ((UndoableEdit)cursor.nextElement()).redo();
	}
    }

    /**
     * Returns the last <code>UndoableEdit</code> in 
     * <code>edits</code>, or <code>null</code>
     * if <code>edits</code> is empty.
     */
    protected UndoableEdit lastEdit() {
	int count = edits.size();
	if (count > 0)
	    return (UndoableEdit)edits.elementAt(count-1);
	else
	    return null;
    }

    /**
     * Sends <code>die</code> to each subedit,
     * in the reverse of the order that they were added.
     */
    public void die() {
	int size = edits.size();
	for (int i = size-1; i >= 0; i--)
	{
	    UndoableEdit e = (UndoableEdit)edits.elementAt(i);
// 	    System.out.println("CompoundEdit(" + i + "): Discarding " +
// 			       e.getUndoPresentationName());
	    e.die();
	}
	super.die();
    }	

    /**
     * If this edit is <code>inProgress</code>,
     * accepts <code>anEdit</code> and returns true.
     * 
     * <p>The last edit added to this <code>CompoundEdit</code>
     * is given a chance to <code>addEdit(anEdit)</code>.
     * If it refuses (returns false), <code>anEdit</code> is
     * given a chance to <code>replaceEdit</code> the last edit.
     * If <code>anEdit</code> returns false here,
     * it is added to <code>edits</code>.
     *
     * @param anEdit the edit to be added
     * @return true if the edit is <code>inProgress</code>;
     *	otherwise returns false
     */ 
    public boolean addEdit(UndoableEdit anEdit) {
	if (!inProgress) {
	    return false;
	} else {
	    UndoableEdit last = lastEdit();

	    // If this is the first subedit received, just add it.
	    // Otherwise, give the last one a chance to absorb the new
	    // one.  If it won't, give the new one a chance to absorb
	    // the last one.

	    if (last == null) {
		edits.addElement(anEdit);
	    }
	    else if (!last.addEdit(anEdit)) {
		if (anEdit.replaceEdit(last)) {
		    edits.removeElementAt(edits.size()-1);
		}
		edits.addElement(anEdit);
	    }

	    return true;
	}
    }

    /**
     * Sets <code>inProgress</code> to false.
     * 
     * @see #canUndo
     * @see #canRedo
     */
    public void end() {
	inProgress = false;
    }

    /**
     * Returns false if <code>isInProgress</code> or if super
     * returns false.
     * 
     * @see	#isInProgress
     */
    public boolean canUndo() {
	return !isInProgress() && super.canUndo();
    }

    /**
     * Returns false if <code>isInProgress</code> or if super 
     * returns false.
     * 
     * @see	#isInProgress
     */
    public boolean canRedo() {
	return !isInProgress() && super.canRedo();
    }

    /**
     * Returns true if this edit is in progress--that is, it has not
     * received end. This generally means that edits are still being
     * added to it.
     *
     * @see	#end
     */
    public boolean isInProgress() {
	return inProgress;
    }

    /**
     * Returns true if any of the <code>UndoableEdit</code>s
     * in <code>edits</code> do.
     * Returns false if they all return false.
     */
    public boolean  isSignificant() {
	Enumeration cursor = edits.elements();
	while (cursor.hasMoreElements()) {
	    if (((UndoableEdit)cursor.nextElement()).isSignificant()) {
		return true;
	    }
	}
	return false;
    }

    /**
     * Returns <code>getPresentationName</code> from the
     * last <code>UndoableEdit</code> added to
     * <code>edits</code>. If <code>edits</code> is empty,
     * calls super.
     */
    public String getPresentationName() {
	UndoableEdit last = lastEdit();
	if (last != null) {
	    return last.getPresentationName();
	} else {
	    return super.getPresentationName();
	}
    }
        
    /**
     * Returns <code>getUndoPresentationName</code>
     * from the last <code>UndoableEdit</code>
     * added to <code>edits</code>.
     * If <code>edits</code> is empty, calls super.
     */
    public String getUndoPresentationName() {
	UndoableEdit last = lastEdit();
	if (last != null) {
	    return last.getUndoPresentationName();
	} else {
	    return super.getUndoPresentationName();
	}
    }
        
    /**
     * Returns <code>getRedoPresentationName</code>
     * from the last <code>UndoableEdit</code>
     * added to <code>edits</code>.
     * If <code>edits</code> is empty, calls super.
     */
    public String getRedoPresentationName() {
	UndoableEdit last = lastEdit();
	if (last != null) {
	    return last.getRedoPresentationName();
	} else {
	    return super.getRedoPresentationName();
	}
    }
        
    /**
     * Returns a string that displays and identifies this
     * object's properties.
     *
     * @return a String representation of this object
     */
    public String toString()
    {
	return super.toString()
	    + " inProgress: " + inProgress
	    + " edits: " + edits;
    }
}
