package dods.clients.importwizard;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.*;
import java.util.*;
//import java.util.Timer;
//import java.util.TimerTask;
/**
* This is the main interface for DodsImport. It allows the user to enter or
* select Dods URLs and then further constrain them using other classes
*
* @author rhonhart
*/
public class URLBuilder extends JPanel
implements TreeSelectionListener
{
Hashtable panels;
ActionHandler handler;
Object[] oldSelectedValues;
JPanel currentPanel;
SearchWindow search;
JPanel urlPanel;
JTextField urlField;
JButton urlSelectorButton;
//JPanel inventoryListPanel;
JPanel urlListPanel;
URLList urlList;
JSplitPane splitPane;
javax.swing.Timer timer;
DodsURL baseURL;
/**
* Construct a <code>URLBuilder</code>
*/
public URLBuilder() {
super();
panels = new Hashtable();
handler = new ActionHandler();
initGUI();
}
/**
* Initialize the GUI components of the class.
*/
private void initGUI() {
urlList = new URLList();
//inventoryScroller = new JScrollPane(inventoryList);
urlListPanel = new JPanel();
urlField = new JTextField("http://dodsdev.gso.uri.edu/cgi-bin/dods-3.1/nph-ff/avhrr.catalog");
urlSelectorButton = new JButton("Search...");
urlSelectorButton.setToolTipText("Click this button to enter the search interface for different database.");
urlPanel = new JPanel();
search = null;
setLayout(new BorderLayout());
currentPanel = new JPanel();
currentPanel.setPreferredSize(new Dimension(480, 300));
currentPanel.setMinimumSize(new Dimension(20,200));
//
// Setup the URL List.
//
//?GridBagLayout gridbag = new GridBagLayout();
//?GridBagConstraints c = new GridBagConstraints();
//?urlListPanel.setLayout(gridbag);
//?c.fill = GridBagConstraints.BOTH;
urlListPanel.setLayout(new BorderLayout());
urlList.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "DODS URLs"));
urlList.addTreeSelectionListener(this);
urlListPanel.setPreferredSize(new Dimension(280,300));
urlListPanel.setMinimumSize(new Dimension(20,200));
urlListPanel.add(urlList, BorderLayout.CENTER);
//
// Setup the field to enter the url
//
urlPanel.setLayout(new BoxLayout(urlPanel, BoxLayout.X_AXIS));
urlPanel.setBorder(BorderFactory.createEmptyBorder(3,3,3,3));
urlPanel.add(new JLabel("DODS URL: "));
urlField.addActionListener(handler);
urlField.setActionCommand("urlField");
urlPanel.add(urlField);
urlPanel.add(Box.createRigidArea(new Dimension(30, 0)));
urlSelectorButton.setActionCommand("select");
urlSelectorButton.addActionListener(handler);
urlPanel.add(urlSelectorButton);
//
//set up the split panel at the buttom
//
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
currentPanel, urlListPanel);
splitPane.setOneTouchExpandable(true);
//splitPane.setDividerLocation(0.75);
splitPane.resetToPreferredSizes();
//
// Add all the panels to the main window
//
add(urlPanel, BorderLayout.NORTH);
//add(urlListPanel, BorderLayout.EAST);
add(splitPane, BorderLayout.CENTER);
}
/**
* Find the indices of <code>objects</code> in <code>ListModel list</code>.
* @param objects A Vector of objects whos index you want to find.
* @param list The ListModel to look for the elements of
* <code>objects</code> in.
*/
protected static int[] getIndicesOfObjects(Vector objects,
ListModel list)
{
int[] indices = new int[0];
int lastIndex = -1;
int index = 0;
if(objects != null && objects.size() > 0) {
indices = new int[objects.size()];
for(int i=0; i<objects.size(); i++) {
for(int j=lastIndex+1; j<list.getSize(); j++) {
if(list.getElementAt(j).equals(objects.elementAt(i))) {
lastIndex = j;
indices[index++] = j;
break;
}
}
}
}
if(indices.length != index) {
int[] newIndices = new int[index];
for(int i=0;i<index;i++)
newIndices[i] = indices[i];
indices = newIndices;
}
return indices;
}
/**
* Applies the constraint expression for the current URL to all of
* the URLs selected in the URLList.
*/
public void applyToSelectedURLs() {
if(currentPanel instanceof DataURLProcessor) {
String ce = ((DataURLProcessor)currentPanel).getURL().getConstraintExpression();
for(int i=0;i<oldSelectedValues.length;i++) {
if(oldSelectedValues[i] instanceof DodsURL)
((DodsURL)oldSelectedValues[i]).setConstraintExpression(ce);
}
}
}
/**
* The function that gets called for TreeSelectionEvents.
*/
public void valueChanged(TreeSelectionEvent e) {
JTree activeTree = (JTree)e.getSource();
TreePath t = activeTree.getSelectionPath();
// Make sure we don't call this twice for the same selection
// and make sure that there is in fact something selected.
if(t != null && t.getPathCount() > 0
&& ((DefaultMutableTreeNode)t.getLastPathComponent()).getUserObject()
instanceof DodsURL)
{
baseURL = (DodsURL)((DefaultMutableTreeNode)t.getLastPathComponent()).getUserObject();
JPanel proc;
// Check to see if we've already created an interface for this
// url. If we haven't, create an instance of the class defined
// by baseURL.getProcessorName().
if((proc = (JPanel)panels.get(baseURL)) == null) {
InterfaceCreator creator = new InterfaceCreator(baseURL);
// Create the message the user sees while the
// interface is being created.
proc = creator.createTempPanel();
//getSize may cause trouble -- sometimes it's 0
//proc.setPreferredSize(currentPanel.getSize());
//proc.setMinimumSize(currentPanel.getSize());
proc.setPreferredSize(currentPanel.getPreferredSize());
proc.setMinimumSize(currentPanel.getMinimumSize());
splitPane.setVisible(false);
proc.setVisible(true);
//currentPanel.setVisible(false);
//remove(currentPanel);
currentPanel = proc;
splitPane.setLeftComponent(proc);
splitPane.setVisible(true);
//add(proc, BorderLayout.CENTER);
// Allows 30 seconds to create the interface
timer = new javax.swing.Timer(30000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
/*
* Create a "time's up" panel that can be displayed
* when the interface creating has taken too long and
* might have failed.
*/
//Change to "time's up" panel only if the temporary panel is currently shown
if (currentPanel.getName() != null &&
currentPanel.getName().equals("temp")) {
System.out.println("Time's up!");
//
// create a panel to notify the failure
//
JPanel temp = new JPanel();
temp.setLayout(new BoxLayout(temp, BoxLayout.Y_AXIS));
temp.setBorder(BorderFactory.createEmptyBorder(10,20,10,10));
temp.add(Box.createVerticalGlue());
JLabel text = new JLabel("<html><font color=\"black\"><center><p>"
+ "<b>Time is up for this request!</b><p>"
+ "Requested to create an interface "
+ "for the Dods Inventory:<p>"
+ baseURL.toString()
+ "</center></font>"
);
text.setAlignmentX(Component.CENTER_ALIGNMENT);
temp.add(text);
temp.add(Box.createVerticalGlue());
//set size
temp.setPreferredSize(currentPanel.getPreferredSize());
temp.setMinimumSize(currentPanel.getMinimumSize());
//
//add temp
//
splitPane.setVisible(false);
temp.setVisible(true);
currentPanel = temp;
splitPane.setLeftComponent(temp);
splitPane.setVisible(true);
getRootPane().getContentPane().validate();
}
timer.stop();
}
});
timer.start();
//timer.schedule(new failMessage(baseURL), 30000);
// Start up the thread to create the interface
creator.start();
}
else {
if(proc instanceof DataURLProcessor)
((CEGenerator)proc).updateCE();
//getSize may cause trouble -- sometimes it's 0
//proc.setPreferredSize(currentPanel.getSize());
//proc.setMinimumSize(currentPanel.getSize());
proc.setPreferredSize(currentPanel.getPreferredSize());
proc.setMinimumSize(currentPanel.getMinimumSize());
//currentPanel.setVisible(false);
splitPane.setVisible(false);
proc.setVisible(true);
//remove(currentPanel);
currentPanel = proc;
splitPane.setLeftComponent(proc);
splitPane.setVisible(true);
//add(proc, BorderLayout.CENTER);
}
getRootPane().getContentPane().repaint();
}
else if(t == null) {
// This means that there are no urls selected, and we should
// get rid of whatever interface is currently open
splitPane.setVisible(false);
//currentPanel.setVisible(false);
//remove(currentPanel);
JPanel temp = new JPanel();
//getSize may cause trouble -- sometimes it's 0
//temp.setPreferredSize(currentPanel.getSize());
//temp.setMinimumSize(currentPanel.getSize());
temp.setPreferredSize(currentPanel.getPreferredSize());
temp.setMinimumSize(currentPanel.getMinimumSize());
add(temp, BorderLayout.CENTER);
currentPanel = temp;
splitPane.setLeftComponent(temp);
splitPane.setVisible(true);
validate();
}
oldSelectedValues = urlList.getSelectedValues();
}
/**
* Returns an array of fully constrained URLs.
* @return an array of fully constrained URLs.
*/
public DodsURL[] getURLs() {
return urlList.getURLs();
}
/**
* This class handles actions thrown by the various GUI componentes
* of <code>URLBuilder</code>
*/
private class ActionHandler extends Object implements ActionListener {
public ActionHandler() {
}
/**
* This function must be defined by all action listeners
* @param e The event.
*/
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
// Action to gather urls
if(command.equals("doneProcessing")) {
doneProcessing(e);
}
// Action to add a url from the url field
else if(command.equals("urlField")) {
urlField(e);
}
// Bring up the selection box
else if(command.equals("select")) {
select(e);
}
// Get urls from the search window
else if(command.equals("foundURLs")) {
foundURLs(e);
}
}
/**
* Called when a URLProcessor is done processing. If the URLProcessor
* is a CEGenerator, get the CE from it. Otherwise get the URLs and
* add them to the urlList.
* @param e The event.
*/
protected void doneProcessing(ActionEvent e) {
Object proc = e.getSource();
if(proc instanceof DataURLProcessor)
applyToSelectedURLs();
else if(proc instanceof InventoryURLProcessor) {
DodsURL invURL = (DodsURL)urlList.getSelectedValue();
DodsURL[] urls = ((InventoryURLProcessor)proc).getURLs();
Vector urlVector;
invURL.setProcessed(true);
if(urlList.getURLCount(invURL) != 0) {
int option = JOptionPane.showConfirmDialog(URLBuilder.this, "Do you want to replace the old urls selected from this inventory?", "Replace old URLs?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if(option == JOptionPane.YES_OPTION) {
urlList.removeAllURLsFromInventory(invURL);
}
}
urlList.addURLsToInventory(urls, invURL);
}
getRootPane().getContentPane().repaint();
}
/**
* Get a URL from the textField at the top of the screen, figure
* out what type of URL it is, and add it to the appropriate list.
* @param e The event.
*/
public void urlField(ActionEvent e) {
if(urlField.getText().endsWith("/")) {
DodsURL url = new DodsURL(urlField.getText(), DodsURL.DIRECTORY_URL);
DodsDirectory dir = new DodsDirectory(url);
urlList.addInventory(url);
dir.setActionCommand("doneProcessing");
dir.addActionListener(handler);
panels.put(url, dir);
// Selecting the url in the inventory list will tell the
// program to create that interface and display it.
// inventoryList.setSelectedIndex(inventoryModel.size() - 1);
}
else {
DodsURL url = new DodsURL(urlField.getText(), DodsURL.CATALOG_URL);
Inventory inv = new Inventory(url);
if(inv.isFileserver()) {
panels.put(url, inv);
urlList.addInventory(url);
inv.setActionCommand("doneProcessing");
inv.addActionListener(handler);
}
else {
urlList.addURL(new DodsURL(urlField.getText(), DodsURL.DATA_URL));
}
}
}
/**
* Open up the search window. If this is the first time the button
* was pressed, a new search window will be created. Otherwise the
* old search window will simply be made visible.
* @param e The event.
*/
protected void select(ActionEvent e) {
if(search == null) {
try {
search = new SearchWindow();
search.addActionListener(handler);
search.setActionCommand("foundURLs");
search.setLocation(50,50);
search.setVisible(true);
}
catch(java.io.FileNotFoundException excp) {
JOptionPane.showMessageDialog(URLBuilder.this,
excp.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
}
}
else
search.setVisible(true);
}
/**
* Get URLs back from the search window and add them to the correct
* list. Bring up the interface for the first URL that was returned.
* @param e The event.
*/
protected void foundURLs(ActionEvent e) {
DodsURL[] urls = search.getURLs();
search.setVisible(false);
if(urls != null) {
for(int i=0;i<urls.length;i++) {
if(urls[i].getType() == DodsURL.CATALOG_URL
|| urls[i].getType() == DodsURL.DIRECTORY_URL)
{
urlList.addInventory(urls[i]);
}
else {
urlList.addURL(urls[i]);
}
}
}
}
}
/**
* This class is used to create interfaces to dods inventory URLs in
* a non-blocking manner.
*/
public class InterfaceCreator extends Thread
implements ActionListener
{
private DodsURL baseURL;
private boolean drawPanel;
/**
* Create an <code>InterfaceCreator</code>
* @param url The url to create an interface for.
*/
public InterfaceCreator(DodsURL url) {
baseURL = url;
drawPanel = true;
}
/**
* Creates the interface, and adds it to the main panel when
* it's done.
*/
public void run() {
Class procClass;
Class[] paramTypes;
Object[] params = { };
java.lang.reflect.Constructor procConstructors[];
JPanel proc = null;
try {
System.out.println("Creating a " + baseURL.getProcessorName());
procClass = Class.forName(baseURL.getProcessorName());
procConstructors = procClass.getConstructors();
// The first constructor declared in a URLProcessor
// is the one that will be called
paramTypes = procConstructors[0].getParameterTypes();
if(paramTypes.length == 1
&& paramTypes[0].getName().endsWith("DodsURL"))
{
params = new Object[1];
params[0] = baseURL;
proc = (JPanel)procConstructors[0].newInstance(params);
}
else if(paramTypes.length == 0) {
proc = (JPanel)procConstructors[0].newInstance(params);
}
}
catch(ClassNotFoundException err) { err.printStackTrace(); }
catch(InstantiationException err) { err.printStackTrace(); }
catch(IllegalAccessException err) { err.printStackTrace(); }
catch(java.lang.reflect.InvocationTargetException err) {
err.getTargetException().printStackTrace();
}
// Make sure that proc has in fact been created and the user
// still wants to see it before drawing it or adding it to any
// hash tables.
if(proc != null && drawPanel) {
//set size before adding to hash table
//Use currentPanel.getSize may cause trouble-- sometimes it's 0
proc.setPreferredSize(currentPanel.getPreferredSize());
proc.setMinimumSize(currentPanel.getMinimumSize());
if(proc instanceof InventoryURLProcessor) {
((InventoryURLProcessor)proc).setActionCommand("doneProcessing");
((InventoryURLProcessor)proc).addActionListener(handler);
}
panels.put(baseURL, proc);
proc.setOpaque(true);
//proc.setPreferredSize(currentPanel.getSize());
//proc.setMinimumSize(currentPanel.getSize());
//currentPanel.setVisible(false);
splitPane.setVisible(false);
proc.setVisible(true);
//remove(currentPanel);
currentPanel = proc;
splitPane.setLeftComponent(proc);
splitPane.setVisible(true);
//add(proc, BorderLayout.CENTER);
getRootPane().getContentPane().validate();
}
}
/**
* The function to handle action events.
*/
public void actionPerformed(ActionEvent e) {
// Instead of actually killing the thread when the user hits
// cancel (which is really messy), just set a boolean telling
// the object not to display and let the thread die quietly.
if(e.getActionCommand().equals("cancel")) {
drawPanel = false;
JPanel temp = new JPanel();
temp.setLayout(new BoxLayout(temp, BoxLayout.Y_AXIS));
temp.add(Box.createVerticalGlue());
JLabel text = new JLabel("<html><font color=\"black\"><b>"
+ " Request cancelled</b></font>");
text.setAlignmentX(Component.CENTER_ALIGNMENT);
temp.add(text);
temp.add(Box.createVerticalGlue());
splitPane.setVisible(false);
//currentPanel.setVisible(false);
//remove(currentPanel);
currentPanel = temp;
splitPane.setLeftComponent(temp);
splitPane.setVisible(true);
//add(temp, BorderLayout.CENTER);
validate();
}
}
/**
* Create a temporary panel that can be displayed while the interface
* is being created.
* @return A temporary panel to show while the interface is being
* created.
*/
public JPanel createTempPanel() {
JPanel temp = new JPanel();
//Set name for later recognization
temp.setName("temp");
JButton cancelButton = new JButton("Cancel");
temp.setLayout(new BoxLayout(temp, BoxLayout.Y_AXIS));
temp.setBorder(BorderFactory.createEmptyBorder(10,20,10,10));
temp.add(Box.createVerticalGlue());
JLabel text = new JLabel("<html><font color=\"black\"><center><p> "
+ "Please wait, creating an interface "
+ "for the Dods Inventory:<p>"
+ baseURL.toString()
+ "</center></font>"
);
text.setAlignmentX(Component.CENTER_ALIGNMENT);
temp.add(text);
cancelButton.addActionListener(this);
cancelButton.setActionCommand("cancel");
cancelButton.setAlignmentX(Component.CENTER_ALIGNMENT);
temp.add(cancelButton);
temp.add(Box.createVerticalGlue());
return temp;
}
}
}