// Copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.

package com.sun.solarisinstall.panels;

import java.util.*;
import java.lang.*;

/**
 * TableData class is the data class for the <CODE>Table</CODE> component.<P>
 *
 * You can run an example program or view an example program
 * by using the following selections:<p>
 *
 * <menu>
 *  <li> <a href="test/TableDataTest.html">Run Example Program</a>
 *  <li> <a href="test/TableDataTest.java">View Example Program Code</a>
 * </menu>
 *
 * @version	1.6	10/04/96
 * @author	Sun Microsystems, Inc.
 * @see sunw.admin.avm.base.Data
*/
public class TableData extends Data {
    protected Vector data;
    private static final String sccs_id = "@(#)TableData.java 1.6 96/10/04 SMI";


    /**
     * Constructs <CODE>TableData</CODE> from an array of <CODE>Vectors</CODE>.
     */  
    public TableData(Vector theData[]) {
	super();
	Assert.notNull(theData);
	data = new Vector();
	for (int i=0; i<theData.length; i++) {
		data.addElement(theData[i]);
        }
    }

    /**
     * Constructs <CODE>TableData</CODE> from an array of strings and
     * a delimeter separating the fields in each string.
     */  
    public TableData(String theData[], String delimeter) {
	super();
	Assert.notNull(theData);
	Assert.notNull(delimeter);
	data = new Vector();
	for (int i = 0; i < theData.length; i++) {
	    data.addElement(tokenize(theData[i], delimeter));
	}
    }

    /**
     * Constructs <CODE>TableData</CODE> from a <CODE>Vector</CODE> of
     * <CODE>Vectors</CODE>.
     */  
    public TableData(Vector theData) {
	super();
	Assert.notNull(theData);
	data = theData;
	if (theData.size() != 0) 
	    Assert.notFalse(theData.elementAt(0) instanceof Vector,
			    "theData is not a Vector of Vectors");
    }

    /**
     * Add to the data.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>item</VAR> New item.
     */  
    public synchronized void addItem(Vector item) {
	data.addElement(item);
	changed();
    }

    /**
     * Add to the data.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>item</VAR> New item.
     * @param <VAR>delimeter</VAR> The delimeter.
     */  
    public synchronized void addItem(String item, String delimeter) {
	data.addElement(tokenize(item, delimeter));
	changed();
    }

    /**
     * Clones Data. The <CODE>Vector</CODE> elements are <strong>not</strong> 
     * cloned.
     * @return 		The clone.
     */  
    public synchronized Object clone() {
	return new TableData((Vector)data.clone());
    }    
	
    /**
     * Returns the <CODE>Data</CODE> as an <CODE>Enumeration</CODE>. 
     * @return 		the data 
     */ 
    public Enumeration elements() { return data.elements(); }

    /**
     * Returns the <CODE>Data</CODE> as an <CODE>Array</CODE>
     * of <CODE>Vectors</CODE>. 
     * @return 		The data.
     */
    public Vector[] getAsArray() { 
	Vector[] dataArray = new Vector[data.size()];
	for (int i=0; i<dataArray.length; i++) {
		dataArray[i] = (Vector)data.elementAt(i);
        }
	return dataArray;
    }

    /**
     * Gets a item in the <CODE>Data</CODE>.  The first item that
     * contains <VAR>object</VAR> will be returned.
     * @param <VAR>object</VAR> The object in item to match.
     * @return 		The item.
     */  
    public Vector getItem(Object object) {
	Assert.notNull(object);
	for (int i = 0; i < data.size(); i++) {
	    if (((Vector)data.elementAt(i)).contains(object)) {
		return (Vector)data.elementAt(i);
	    }
	}
	return null;
    }

    /**
     * Gets a item in the <CODE>Data</CODE>.  The first item that contains both
     * <VAR>objects</VAR> will be returned.
     * @param <VAR>object</VAR> The object in item to match.
     * @param <VAR>object</VAR> The object in item to match.
     * @return 		The item.
     */  
    public Vector getItem(Object object1, Object object2) {
	Assert.notNull(object1);
	Assert.notNull(object2);
	for (int i = 0; i < data.size(); i++) {
	    if (((Vector)data.elementAt(i)).contains(object1) && 
				((Vector)data.elementAt(i)).contains(object2)) {
		return (Vector)data.elementAt(i);
	    }
	}
	return null;
    }

    /**
     * Removes all items in the <CODE>Data</CODE>.  
     * <CODE>Observers</CODE> are notified of the change.
     */  
    public final synchronized void removeAllItems() {
	data.removeAllElements();
	changed();
    }

    /**
     * Removes an item within the <CODE>Data</CODE>.  The first item
     * that contains object will be removed.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>object</VAR> The object in item to match.
     * @return true If the item was removed; otherwise false.
     */  
    public synchronized boolean removeItem(Object object) {
	Assert.notNull(object);
	for (int i = 0; i < data.size(); i++) {
	    if (((Vector)data.elementAt(i)).contains(object)) {
	    	data.removeElementAt(i);
		changed();
		return true;
	    }
	}
	return false;
    }

    /**
     * Removes a item within the <CODE>Data</CODE>.  The first item
     * that contains both <VAR>objects</VAR> will be removed.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>object</VAR> The object in item to match.
     * @param <VAR>object</VAR> The object in item to match.
     * @return true If the item was removed; otherwise false.
     */  
    public synchronized boolean removeItem(Object object1, Object object2) {
	Assert.notNull(object1);
	Assert.notNull(object2);
	for (int i = 0; i < data.size(); i++) {
	    if (((Vector)data.elementAt(i)).contains(object1) && 
				((Vector)data.elementAt(i)).contains(object2)) {
	    	data.removeElementAt(i);
		changed();
		return true;
	    }
	}
	return false;
    }

    /**
     * Replaces the <CODE>Data</CODE>.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>theData</VAR> New data.
     */  
    public synchronized void replace(Vector theData[]) {
	Assert.notNull(theData);
	data.removeAllElements();
	for (int i=0; i<theData.length; i++) {
		data.addElement(theData[i]);
        }
	changed();
    }

    /**
     * Replaces the <CODE>Data</CODE>.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>theData</VAR> New data.
     * @param <VAR>delimeter</VAR> The delimeter.
     */  
    public synchronized void replace(String theData[], String delimeter) {
	Assert.notNull(theData);
	Assert.notNull(delimeter);
	data.removeAllElements();
	for (int i = 0; i < theData.length; i++) {
	    data.addElement(tokenize(theData[i], delimeter));
	}
	changed();
    }

    /**
     * Replaces the <CODE>Data</CODE>.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>theData</VAR> The new data.
     */  
    public synchronized void replace(Vector theData) {
	Assert.notNull(theData);
	if (theData.size() != 0) 
	    Assert.notFalse(theData.elementAt(0) instanceof Vector,
			    "theData is not a Vector of Vectors");
	data = theData;
	changed();
    }

    /**
     * Replaces a item within the Data.  The first item that contains Object
     * will be replaced.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>object</VAR> The object in item to match.
     * @param <VAR>theData</VAR> The new item.
     * @param <VAR>delimeter</VAR> The delimeter.
     * @return true If the item was replaced; otherwise false.
     */  
    public synchronized boolean replaceItem(Object object, String theData, 
						String delimeter) {
	Assert.notNull(object);
	Assert.notNull(theData);
	Assert.notNull(delimeter);
	for (int i = 0; i < data.size(); i++) {
	    if (((Vector)data.elementAt(i)).contains(object)) {
	    	data.removeElementAt(i);
		data.insertElementAt(tokenize(theData, delimeter), i);
		changed();
		return true;
	    }
	}
	return false;
    }

    /**
     * Replaces an item within the <CODE>Data</CODE>.  The first item
     * that contains <VAR>object</VAR> will be replaced.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>object</VAR> The object in item to match.
     * @param <VAR>theData</VAR> The new item.
     * @return true If the item was replaced; otherwise false.
     */  
    public synchronized boolean replaceItem(Object object, Vector theData) {
	Assert.notNull(object);
	Assert.notNull(theData);
	for (int i = 0; i < data.size(); i++) {
	    if (((Vector)data.elementAt(i)).contains(object)) {
	    	data.removeElementAt(i);
	    	data.insertElementAt(theData, i);
		changed();
		return true;
	    }
	}
	return false;
    }

    /**
     * Replaces an item within the <CODE>Data</CODE>.  The first item
     * that contains both <VAR>objects</VAR> will be replaced.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>object1</VAR> The object in item to match.
     * @param <VAR>object2</VAR> The object in item to match.
     * @param <VAR>theData</VAR> The new item.
     * @param <VAR>delimeter</VAR> The delimeter.
     * @return true If the item was replaced; otherwise false.
     */  
    public synchronized boolean replaceItem(Object object1, Object object2,
				String theData, String delimeter) {
	Assert.notNull(object1);
	Assert.notNull(object2);
	Assert.notNull(theData);
	Assert.notNull(delimeter);
	for (int i = 0; i < data.size(); i++) {
	    if (((Vector)data.elementAt(i)).contains(object1) && 
				((Vector)data.elementAt(i)).contains(object2)) {
	    	data.removeElementAt(i);
	    	data.insertElementAt(tokenize(theData, delimeter), i);
		changed();
		return true;
	    }
	}
	return false;
    }

    /**
     * Replaces a item within the <CODE>Data</CODE>.  The first item
     * that contains both <VAR>objects</VAR> will be replaced.
     * <CODE>Observers</CODE> are notified of the change.
     * @param <VAR>object1</VAR> The object in item to match.
     * @param <VAR>object2</VAR> The object in item to match.
     * @param <VAR>theData</VAR> The new item.
     * @return true If the item was replaced; otherwise false.
     */  
    public synchronized boolean replaceItem(Object object1, Object object2, 
							Vector theData) {
	Assert.notNull(object1);
	Assert.notNull(object2);
	Assert.notNull(theData);
	for (int i = 0; i < data.size(); i++) {
	    if (((Vector)data.elementAt(i)).contains(object1) && 
				((Vector)data.elementAt(i)).contains(object2)) {
	    	data.removeElementAt(i);
	    	data.insertElementAt(theData, i);
		changed();
		return true;
	    }
	}
	return false;
    }

    /**
     * Returns the number of items in the <CODE>Data</CODE>.
     * @return The size of <CODE>Data</CODE>.
     */
    public final int size() { return data.size(); }
  

    /**
     * Converts <CODE>Data</CODE> to a rather lengthy <CODE>String</CODE>. 
     * Thi is useful for debugging.
     * @return A string.
     */  
    public synchronized String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append("{");
        for (int i = 0; i < data.size(); i++) {
            if (i >= 1) buf.append(",");
            buf.append(((Vector)data.elementAt(i)).toString());
        }     
        buf.append("}");
        return buf.toString();
    }

    /**
     * Returns the version of this class.
     */
    public String getClassVersion() {
        return (sccs_id);
    }

    /**
     * Tokenize the argument <CODE>String</CODE> by breaking it apart using
     * the argument delimeter.  The method detects empty columns and inserts
     * a 0-length <CODE>String</CODE> into the returned <CODE>Vector</CODE>.
     * A delimeter as the first character in the data is interpreted as an
     * empty first column -- a delimeter as the last character as an empty
     * last column.  If the data is empty, it is treated as containing a
     * single, empty column.
     * @param <VAR>data</VAR> A <CODE>String</CODE> containing fields
     * separated by a delimeter.
     * @param <VAR>delimeter</VAR> The delimeter separating the fields.
     * @return A <CODE>Vector</CODE> containing an element for each field.
     */
    private static Vector tokenize(String data, String delimeter) {
        Vector v = new Vector();
        StringTokenizer tok = new StringTokenizer(data, delimeter, true);

        boolean onDelim = true; // beginning of String is treated as delimeter
        String token = null;

        while (tok.hasMoreTokens()) {
            token = tok.nextToken();

            if (delimeter.equals(token)) {
                if (onDelim == true)
                    // If the last thing we saw was a delimeter, this
                    // column is empty

                    v.addElement("");
                else
                    onDelim = true;
            } else {
                onDelim = false;
                v.addElement(token);
            }
        }

        // Finally, if the last token we saw was a delimeter, the
        // last column is empty.

        if (token == null || delimeter.equals(token))
            v.addElement("");

        return v;
    }

}

