package org.jcon.util;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Vector;

/**
 * Various data related static methods.
 *
 * @author Jack Harich
 */
public class DataLib {

//---------- Initialization ------------------------------
public static void main(String args[]) {
    String text = "123456";
    String result = removeLastCharacters(text, 2);
    print(" - text = " + text + ", result of removing last 2 = " + result);
}
//---------- Public Methods ------------------------------
/**
 * Converts the Vector of Strings to an array and returns
 * the sorted array.
 */
public static String[] sortStringVectorToArray(Vector vector) {
    String[] items = new String[vector.size()];
    for (int i = 0; i < items.length; i++) {
        items[i] = (String)vector.elementAt(i);
    }
    ArraySorter.sortASCII(items);
    return items;
}
/**
 * Returns last token in delimited string, interned.
 * Example: name = DataLib.getLastToken(className, ".");
 */
public static String getLastToken(String path, String delimiter) {
    StringTokenizer tokens = new StringTokenizer(path, delimiter);
    String lastToken = "#Error#";
    // Now set lastToken to last token
    while (tokens.hasMoreTokens()) {
        lastToken = tokens.nextToken();
    }
    return lastToken.intern();
}
/**
 * Returns the delimited field number using base 1, or null
 * if no such field number exists. The returned String is
 * not trimmed or interned.
 */
public static String getDelimitedField(String fields,
        int fieldNumber, String delimiters) {
    StringTokenizer tokens = new StringTokenizer(fields, delimiters);
    int count = 0;
    while (tokens.hasMoreTokens()) {
        String token = tokens.nextToken();
        count++;
        if (count == fieldNumber) return token;
    }
    return null;
}
// Isn't this in Java somewhere?
public static boolean isInt(String text) {
    try {
        int value = Integer.parseInt(text);
        return true;
    } catch(Exception ex) {
        return false;
    }
}
/**
 * Returns true if @param text equals "true" or "false",
 * otherwise  returns false. Note this differs from
 * Boolean.getBoolean(String) which considers only "true".
 */
public static boolean isBoolean(String text) {
    if (text.equals("true") || text.equals("false")) {
        return true;
    } else {
        return false;
    }
}
/**
 * Builds and returns a String array from the delimItems
 * using the delimters. A SringTokenizer is
 * constructed with (delimItems, delimeters), so the
 * arguments should adhere to those expectations. Common
 * delimiers are "," and ", ". Null delimItems will
 * return a zero length array.
 */
public static String[] convertDelimStringToArray(
        String delimItems, String delimiters) {
    //print(".convertDelimStringToArray() - delimItems = " + delimItems);
    if (delimItems == null) return new String[0];
    
    StringTokenizer tokens = new StringTokenizer(delimItems, delimiters);
    String[] items = new String[tokens.countTokens()];
    int i = 0;
    while (tokens.hasMoreTokens()) {
        items[i] = tokens.nextToken();
        i++;
    }
    return items;
}
/**
 * Builds and returns a Vector form the delimItems using
 * the delimiters. Null delimItems will return an empty
 * Vector. Typical delimiters are ". ".
 */
public static Vector convertDelimStringToVector(
        String delimItems, String delimiters) {

    if (delimItems == null) return new Vector();

    StringTokenizer tokens = new StringTokenizer(delimItems, delimiters);
    Vector items = new Vector();
    while (tokens.hasMoreTokens()) {
        items.addElement(tokens.nextToken());
    }
    return items;
}
/**
 * Builds and returns a delimited String using the Objects
 * in the Vector, separated by the delimiter. If the
 * Objects are not Strings then their toString() is used.
 * A common delimiter is ", ". A delimiter of "\n" will
 * do the obvious.
 */
public static String convertVectorToDelimString(
        Vector items, String delimiter) {

    StringBuffer delimItems = new StringBuffer();
    int count = items.size();
    for (int i = 0; i < count; i++) {
        String item;
        Object object = items.elementAt(i);
        if (object instanceof String) {
            item = (String)items.elementAt(i);
        } else {
            item = object.toString();
        }
        delimItems.append(item);
        // Add delimiter if more
        if (i < count - 1) delimItems.append(delimiter);
    }
    // A future optimization involving changing the add delimiter
    // Truncate the last, unneeded delimiter
    //delimItems.setLength(delimItems.length() - delimiter.length());

    return delimItems.toString();
}
/**
 * Returns the word with its first letter capatilized.
 * A zero length word will return "".
 */
public static String upperCaseFirstLetter(String word) {
    if (word.length() == 0) return "";
    String firstLetter = word.substring(0, 1).toUpperCase();
    if (word.length() == 1) {
        return firstLetter;
    } else {
        return firstLetter + word.substring(1);
    }
}
/**
 * Converts the Enumeration to a String array. If an
 * Enumeration object is not a String it's toString()
 * method is used to convert it to a String. The
 * size must be the size of the Enumeration.
 * (*** could mod so that that if size < 0 then it first
 * fills a Vector and then coverts that.)
 */
public static String[] convertEnumerationToStringArray(
            Enumeration enum, int size) {
    String[] items = new String[size];
    int index = 0;
    while (enum.hasMoreElements()) {
        Object item = enum.nextElement();
        if (item instanceof String) {
            items[index++] = (String)item;
        } else {
            items[index++] = item.toString();
        }
    }
    return items;
}
/**
* Converts the Enumeration to a String, using the preferred
* delimiter, which is usually ", " or "\n". If isSorted is
* true then the result is sorted.
*/ 
public static String convertEnumerationToString(
    Enumeration enum, String delimiter, boolean isSorted) {
            
    StringBuffer buffer = new StringBuffer();
    if (isSorted) {
        Vector vectorItems = new Vector();
        while (enum.hasMoreElements()) {
            vectorItems.addElement(enum.nextElement());
        }
        String[] items = convertVectorToStringArray(vectorItems);
        ArraySorter.sortASCII(items);
        for (int i = 0; i < items.length; i++) {
            buffer.append(items[i]);
            buffer.append(delimiter);
        }   
    } else {   
        // Not sorted
        while (enum.hasMoreElements()) {
            Object item = enum.nextElement();
            if (item instanceof String) {
                buffer.append((String)item);
            } else {
                buffer.append(item.toString());
            }
            buffer.append(delimiter);
        }    
    }
    return buffer.toString();
}            
/**
 * Like convertEnumerationToStringArray() except input is
 * a Vector.
 */
public static String[] convertVectorToStringArray(
            Vector vector) {
    String[] items = new String[vector.size()];
    int index = 0;
    for (int i = 0; i < vector.size(); i++) {
        Object item = vector.elementAt(i);
        if (item instanceof String) {
            items[index++] = (String)item;
        } else {
            items[index++] = item.toString();
        }
    }
    return items;
}
/**
* Gets the stack trace from the throwable and converts it
* to a String.
*/
public static String convertStackTraceToString(Throwable throwable) {
    ByteArrayOutputStream ba = new ByteArrayOutputStream();
    PrintWriter writer = new PrintWriter(ba);
    throwable.printStackTrace(writer);
    writer.flush();
    return ba.toString();
}
/**
 * Returns the first substring followed by the delimiter.
 * For example use this to get "First" from "First.Last".
 */
public static String getFirstDelimited(String text, char delimiter) {
    return text.substring(0, text.indexOf(delimiter));
}
/**
 * Returns the last substring preceeded by the delimiter.
 * For example use this to get "Last" from "First.Last".
 */
public static String getLastDelimited(String text, char delimiter) {
    return text.substring(text.lastIndexOf(delimiter) + 1);
}
/**
 * Removes all occurences of removalChar in text.
 */
public static String removeCharacter(String text,
        char removalChar) {
    String newText = "";
    for (int i = 0; i < text.length(); i++) {
        char character = text.charAt(i);
        if (character != removalChar) newText += character;
    }
    return newText;
}
/**
 * Right pads the text with spaces to fill the length.
 * If the text is longer than the length it is truncated.
 */
public static String rightPad(String text, int length) {
    if (text.length() > length) {
        return text.substring(0, length);
    } else {
        return text + fillString(' ', length - text.length());
    }
}
/**
 * Fills a String with the specified character and length.
 */
public static String fillString(char character, int length) {
    char[] array = new char[length];
    for (int i = length; --i >= 0; ) {
        array[i] = character;
    }
    return new String(array);
}
/**
 * Replaces the oldValue with the newValue in text and
 * returns the result. If not found then no change occurs.
 * Neither value should be null. The newValue may be "".
 */
public static String replaceToken(String text,
        String oldValue, String newValue) {
    int index = text.indexOf(oldValue);
    if (index <0) {
        return text;
    } else {
        return text.substring(0, index) +
            newValue + 
            text.substring(index + oldValue.length());   
    }
}    
/**
 * Removes the last numCharacters from text. This is hard
 * to remember how to do, due to Java using 1 base for
 * length and zero base for position, and the fact that
 * the second substring arg is exclusive. Returns "" if the
 * text is equals to or shorter than the numCharacters.
 */
public static String removeLastCharacters(String text, int numCharacters) {
    if (text.length() <= numCharacters) {
        return "";
    } else {
        return text.substring(0, text.length() - numCharacters);
    }
}

/**
 * Returns true if the value is "like" the pattern, which may
 * be a typical String or a wildcard. Currently we support: <BR>
 * - true if value.equals(pattern) <BR>
 * - pattern starts or ends with the wildcard "*" <BR>
 * - Otherwise false is returned.
 *
 * This method is case sensitive. *** UNTESTED ***
 */
public static boolean isLike(String value, String pattern) {
    if (value.equals(pattern)) {
        return true;

    } else if (pattern.startsWith("*")) {
        if (pattern.length() == 1) {
            return true;
        } else {
            return value.endsWith(pattern.substring(1));
        }
    } else if (pattern.endsWith("*")) {
        String match = pattern.substring(0, pattern.length() - 1);
        return value.startsWith(match);

    } else {
        return false; // Not supported
    }
}
/**
 * Returns an int array from the comma and space delimited
 * ints in text, such as "0, 0, 25, 1".
 */
public static int[] createIntArray(String text) {
    StringTokenizer tokens = new StringTokenizer(text, ", ");
    int[] items = new int[tokens.countTokens()];
    int i = 0;
    while (tokens.hasMoreTokens()) {
        String item = tokens.nextToken();
        items[i] = Integer.parseInt(item);
        i++;
    }
    return items;
}
/**
* Appends array2 to the end of array1 and returns the
* result, which is a new and probably larger array.
*/
public static String[] appendStringArray(
                String[] array1, String[] array2) {
    String[] newArray = new String[array1.length + array2.length];
    System.arraycopy(array1, 0, newArray, 0, array1.length);
    System.arraycopy(array2, 0, newArray, array1.length, array2.length);
    return newArray;
}
/**
* Returns true if the array contains the value or false if
* not or array is null. Case insensitive. This is useful
* for checking command line args and such.
*/
public static boolean arrayContainsValueIgnoreCase(
        String[] array, String value) {
         
    if (array == null) return false;
                                
    for (int i = array.length; --i >= 0; ) {
        if (array[i].equalsIgnoreCase(value)) return true;
    }
    return false;
} 
/**
* Appends the contents of vector2 to vector1, preserving
* the order in vector2. No effect if vector2 empty.
*/
public static void appendVector(Vector vector1, Vector vector2) {
    int size = vector2.size();
    for (int i = 0; i < size; i++) {
        vector1.addElement(vector2.elementAt(i));
    }
}
/**
* Returns the Vector of objects in vector1 but not in
* vector2. The result may be empty and can never be more
* than the contents of vector1. The test for equality
* uses the Object.equals(Object) method, which allows
* custom or default differencing.
*/
public static Vector computeVectorDifference(
                        Vector vector1, Vector vector2) {
    Vector difference = new Vector();
    int size = vector1.size();
    for (int i = 0; i < size; i++) {
        Object object1 = vector1.elementAt(i);
        if (! vector2.contains(object1)) {
            difference.addElement(object1);
        }
    }
    return difference;
}
/**
* Inserts the object into the vector, just after the
* previousObject. If the previousObject is null then the
* object is inserted as the first element. If the
* object is already in the vector then it is first removed.
* Thus this method can be used to insert or move.
*/
// *** Not yet tested - 10/13/98 JH
public static void insertIntoVector(Vector vector, 
            Object object, Object previousObject) {
    vector.removeElement(object);
    if (previousObject == null) {
        vector.insertElementAt(object, 0);
    } else {
        int previousIndex = vector.indexOf(previousObject);
        vector.insertElementAt(object, previousIndex + 1);
    }
}
/**
* Removes all occurances of the object in the Hashtable.
* No effect if not found. This should be in Hashable but 
* is not. Hashtable only supports removing using a key.
*/
public static void removeObjectFromHashtable(
                        Hashtable table, Object object) {
    Enumeration keys = table.keys();
    while (keys.hasMoreElements()) {
        Object key = keys.nextElement();
        Object value = table.get(key);
        if (value.equals(object)) table.remove(key);
    }
}
//---------- Private Methods -----------------------------
//--- Std
private static void print(String text) {
    System.out.println("DataLib" + text);
}

} // End class
