/* GPL_HEADER */
package org.greenstone.gatherer.util;

/**************************************************************************************
 * Title:        Gatherer
 * Description:  The Gatherer: a tool for gathering and enriching a digital collection.
 * Company:      The University of Waikato
 * Written:        /  /01
 * Revised:      16/08/02 Improved
 **************************************************************************************/
import java.awt.*;
import java.io.*;
import java.util.*;
import javax.swing.filechooser.*;
import org.w3c.dom.*;

/** This utility class contains a series of static methods for common array type manipulations including appending, casting and changing between containers.
 * @author John Thompson
 * @version 2.3
 */
public class ArrayTools {

    /** Append a Component onto an array of Components. */
    static public Component[] add(Component[] a, Component b) {
	Component[] c = null;
	if(a != null && b != null) {
	    c = new Component[a.length + 1];
	    System.arraycopy(a, 0, c, 0, a.length);
	    c[c.length - 1] = b;
	}
	else if(a == null && b != null) {
	    c = new Component[1];
	    c[0] = b;
	}
	else if(a != null && b == null) {
	    c = a;
	}
	return c;
    }
    /** This method efficiently appends a new element onto the end of a element array.
     * @param a The initial <strong>Element[]</strong>.
     * @param b The new <strong>Element</strong>.
     * @return An <strong>Element[]</strong> containing a followed by b.
     */
    static public Element[] add(Element a[], Element b) {
	Element[] c = null;
	if(a != null && b != null) {
	    c = new Element[a.length + 1];
	    System.arraycopy(a, 0, c, 0, a.length);
	    c[c.length - 1] = b;
	}
	else if(a == null && b != null) {
	    c = new Element[1];
	    c[0] = b;
	}
	else if(a != null && b == null) {
	    c = a;
	}
	return c;
    }
    /** This method efficently appends one element array onto the end of another.
     * @param a The initial <strong>Element[]</strong>.
     * @param b The <strong>Element[]</strong> to append.
     * @return An <strong>Element[]</strong> containing a followed by b.
     */
    static public Element[] add(Element a[], Element b[]) {
	Element[] c = null;
	if(a != null && b != null) {
	    c = new Element[a.length + b.length];
	    System.arraycopy(a, 0, c, 0, a.length);
	    System.arraycopy(b, 0, c, a.length, b.length);
	}
	else if(a == null && b != null) {
	    c = b;
	}
	else if(a != null && b == null) {
	    c = a;
	}
	return c;
    }
    /** This method efficiently appends a new file onto the end of a file array.
     * @param a The initial <strong>File[]</strong>.
     * @param b The new <strong>File</strong>.
     * @return A <strong>File[]</strong> containing a followed by b.
     */
    static public File[] add(File a[], File b) {
	File[] c = null;
	if(a != null && b != null) {
	    c = new File[a.length + 1];
	    System.arraycopy(a, 0, c, 0, a.length);
	    c[c.length - 1] = b;
	}
	else if(a == null && b != null) {
	    c = new File[1];
	    c[0] = b;
	}
	else if(a != null && b == null) {
	    c = a;
	}
	return c;
    }
    /** This method efficently appends one file array onto the end of another.
     * @param a The initial <strong>File[]</strong>.
     * @param b The <strong>File[]</strong> to append.
     * @return A <strong>File[]</strong> containing a followed by b.
     */
    static public File[] add(File a[], File b[]) {
	File[] c = null;
	if(a != null && b != null) {
	    c = new File[a.length + b.length];
	    System.arraycopy(a, 0, c, 0, a.length);
	    System.arraycopy(b, 0, c, a.length, b.length);
	}
	else if(a == null && b != null) {
	    c = b;
	}
	else if(a != null && b == null) {
	    c = a;
	}
	return c;
    }


    /** This method efficiently appends a new node onto the end of a node array.
     * @param a The initial <strong>Node[]</strong>.
     * @param b The new <strong>Node</strong>.
     * @return A <strong>Node[]</strong> containing a followed by b.
     */
    static public Node[] add(Node a[], Node b) {
	Node[] c = null;
	if(a != null && b != null) {
	    c = new Node[a.length + 1];
	    System.arraycopy(a, 0, c, 0, a.length);
	    c[c.length - 1] = b;
	}
	else if(a == null && b != null) {
	    c = new Node[1];
	    c[0] = b;
	}
	else if(a != null && b == null) {
	    c = a;
	}
	return c;
    }
    /** This method efficently appends one node array onto the end of another.
     * @param a The initial <strong>Node[]</strong>.
     * @param b The <strong>Node[]</strong> to append.
     * @return A <strong>Node[]</strong> containing a followed by b.
     */
    static public Node[] add(Node a[], Node b[]) {
	Node[] c = null;
	if(a != null && b != null) {
	    c = new Node[a.length + b.length];
	    System.arraycopy(a, 0, c, 0, a.length);
	    System.arraycopy(b, 0, c, a.length, b.length);
	}
	else if(a == null && b != null) {
	    c = b;
	}
	else if(a != null && b == null) {
	    c = a;
	}
	return c;
    }
	 
    /** This method efficiently appends a new string onto the end of a string array.
     * @param a The initial <strong>String[]</strong>.
     * @param b The new <strong>String</strong>.
     * @return A <strong>String[]</strong> containing a followed by b.
     */
    static public String[] add(String a[], String b) {
	String[] c = null;
	if(a != null && b != null) {
	    c = new String[a.length + 1];
	    System.arraycopy(a, 0, c, 0, a.length);
	    c[c.length - 1] = b;
	}
	else if(a == null && b != null) {
	    c = new String[1];
	    c[0] = b;
	}
	else if(a != null && b == null) {
	    c = a;
	}
	return c;
    }
    /** This method efficently appends one string array onto the end of another.
     * @param a The initial <strong>String[]</strong>.
     * @param b The <strong>String[]</strong> to append.
     * @return A <strong>String[]</strong> containing a followed by b.
     */
    static public String[] add(String a[], String b[]) {
	String[] c = null;
	if(a != null && b != null) {
	    c = new String[a.length + b.length];
	    System.arraycopy(a, 0, c, 0, a.length);
	    System.arraycopy(b, 0, c, a.length, b.length);
	}
	else if(a == null && b != null) {
	    c = b;
	}
	else if(a != null && b == null) {
	    c = a;
	}
	return c;
    }
    /** This method takes an array list and creates a string array.
     * @param a An <strong>ArrayList</strong> containing hopefully <strong>String</strong> or else this will fail.
     * @return A <strong>String[]</strong> or <i>null</i> if the array could not be created.
     */
    static public String[] arrayListToStringArray(ArrayList a) {
	String array[] = new String[a.size()];
	for(int i = 0; i < array.length; i++) {
	    array[i] = (String)a.get(i);
	}
	return array;
    }

    static public File[] filter(File[] files, String pattern, boolean exclude) {
	int write_ptr = 0;
	///ystem.err.println("Filtering by '" + pattern + "', Exclude? " + exclude + " :");
	for(int read_ptr = 0; read_ptr < files.length; read_ptr++) {
	    File current = files[read_ptr];
	    files[write_ptr] = current;
	    ///ystem.err.print("Testing " + current.getName() + " -> ");
	    // Determine whether we move the write pointer or not.
	    if(current.getName().toLowerCase().matches(pattern)) {
		if(!exclude) {
		    ///ystem.err.println("Match, Exclude.");
		    write_ptr++;  
		}
		else {
		    ///ystem.err.println("Match, Include.");
		}
	    }
	    else {
		// You can't exclude folders with an inclusion filter!
		if(exclude || current.isDirectory()) {
		    ///ystem.err.println("Nonmatch, Exclude.");
		    write_ptr++;
		}
		else {
		    ///ystem.err.println("Nonmatch, Include.");
		}
	    }
	}
	File[] result = new File[write_ptr];
	System.arraycopy(files, 0, result, 0, result.length);
	pattern = null;
	files = null;
	return result;
    }


    /** Transforms an Object array into a single string of the form '[o1,o2,...,on]'.
     * @param objects An <strong>Object[]</strong>. Note that the objects in this array must support toString() reasonably.
     * @return A <strong>String</strong> representing the given array.
     */
    static public String objectArrayToString(Object objects[]) {
	StringBuffer result = new StringBuffer("[");
	for(int i = 0; i < objects.length; i++) {
	    result.append(objects[i].toString());
	    if(i < objects.length - 1) {
		result.append(",");
	    }
	}
	result.append("]");
	return result.toString();
    }


    /** Sorts an array of files, putting non-files first. Case insensitive.
     * @param files_to_sort The File[] to be sorted.
     */
    static public void sort(File[] files_to_sort)
    {
	// Return if there is nothing to sort
	if (files_to_sort == null || files_to_sort.length <= 1) {
	    return;
	}

	FileSystemView fileSystemView = FileSystemView.getFileSystemView();
	FileComparator comparator = new FileComparator();

	// Separate the file system roots and directories from the files, and sort separately
	ArrayList files_list = new ArrayList((files_to_sort.length * 3) / 4);
	ArrayList non_files_list = new ArrayList((files_to_sort.length) / 4);
	for (int i = 0; i < files_to_sort.length; i++) {
	    File file = files_to_sort[i];

	    // File is a system root
	    if (fileSystemView.isFileSystemRoot(file)) {
		non_files_list.add(file);
	    }
	    // File is a directory
	    else if (file.isDirectory()) {
		non_files_list.add(file);
	    }
	    // File is an actual file
	    else {
		files_list.add(file);
	    }
	}

	// Sort files
	Object[] files = files_list.toArray();
	Arrays.sort(files, comparator);

	// Sort non-files
	Object[] non_files = non_files_list.toArray();
	Arrays.sort(non_files, comparator);

	// Merge the results, putting non-files before files
	int j = 0;
	for (int i = 0; i < non_files.length; i++) {
	    files_to_sort[j++] = (File) non_files[i];
	}
	for (int i = 0; i < files.length; i++) {
	    files_to_sort[j++] = (File) files[i];
	}
    }


    /** Comparator used to order files. */
    static private class FileComparator
	implements Comparator {

	/** Compare two files in terms of ordering of their paths.
	 * @param o1 The <strong>Object</strong> that represents the first file.
	 * @param o2 The other <strong>Object</strong>, also a file.
	 * @return An <i>int</i> which is <1, 0 or >1 if o1 is <o2, =o2 or >o2 respectively.
	 */
	public int compare(Object o1, Object o2)
	{
	    File f1 = (File) o1;
	    File f2 = (File) o2;

	    return f1.getName().compareToIgnoreCase(f2.getName());
	}
    }
}
