/*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package tufts.vue;
import tufts.vue.ds.XmlDataSource;
import tufts.Util;
import tufts.vue.gui.*;
import tufts.vue.ui.MetaDataPane;
import tufts.vue.ui.ResourceList;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.List;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;
import java.io.*;
import java.util.*;
import org.osid.repository.Asset;
import org.osid.repository.AssetIterator;
import org.osid.repository.RepositoryException;
import edu.tufts.vue.ui.DefaultQueryEditor;
/**
* This class wraps a DataSourceList, and handles communicating user
* selection events on the list to other panes, such as search or browse,
* as well as requesting browse components and loading them into the browse pane.
* Also handles the big task of kicking off parallel searches for multiple sources
* in multiple threads (full parallel federated search).
*/
public class DataSourceViewer extends ContentViewer
implements KeyListener, edu.tufts.vue.fsm.event.SearchListener, ActionListener
{
private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(DataSourceViewer.class);
//private static final boolean UseFederatedSearchManager = false;
public java.util.List<AssetIterator> assets = new ArrayList<AssetIterator>();
private static DRBrowser DRB;
private static Object activeDataSource;
String breakTag = "";
public final static int ADD_MODE = 0;
public final static int EDIT_MODE = 1;
public static final org.osid.shared.Type favoritesRepositoryType = new edu.tufts.vue.util.Type("edu.tufts","favorites","Favorites");
JPopupMenu popup;
AddLibraryDialog addLibraryDialog;
UpdateLibraryDialog updateLibraryDialog;
EditLibraryDialog editLibraryDialog;
RemoveLibraryDialog removeLibraryDialog;
GetLibraryInfoDialog getLibraryInfoDialog;
static AbstractAction checkForUpdatesAction;
static AbstractAction addLibraryAction;
AbstractAction editLibraryAction;
AbstractAction removeLibraryAction;
AbstractAction getLibraryInfoAction;
// public static Vector allDataSources = new Vector();
public static DataSourceList dataSourceList;
// private DockWindow resultSetDockWindow;
private static DockWindow editInfoDockWindow; // hack for now: need this set before DSV is created
//javax.swing.JScrollPane resultSetTreeJSP;
JPanel previewPanel = null;
static edu.tufts.vue.dsm.DataSourceManager dataSourceManager;
//static edu.tufts.vue.dsm.DataSource dataSources[];
static edu.tufts.vue.fsm.FederatedSearchManager federatedSearchManager;
static edu.tufts.vue.fsm.QueryEditor queryEditor;
private edu.tufts.vue.fsm.SourcesAndTypesManager sourcesAndTypesManager;
//private java.awt.Dimension resultSetPanelDimensions = new java.awt.Dimension(400,200);
// private javax.swing.JPanel resultSetPanel = new javax.swing.JPanel();
org.osid.shared.Type searchType = new edu.tufts.vue.util.Type("mit.edu","search","keyword");
org.osid.shared.Type thumbnailType = new edu.tufts.vue.util.Type("mit.edu","partStructure","thumbnail");
ImageIcon noImageIcon;
private JPanel dummyPanel = new JPanel();
private org.osid.OsidContext context = new org.osid.OsidContext();
//org.osid.registry.Provider checked[];
private final java.util.List<SearchThread> mSearchThreads = java.util.Collections.synchronizedList(new java.util.LinkedList<SearchThread>());
private final java.util.List<MapBasedSearchThread> mMapBasedSearchThreads = java.util.Collections.synchronizedList(new java.util.LinkedList<MapBasedSearchThread>());
private static volatile DataSourceViewer singleton;
public DataSourceViewer(DRBrowser drBrowser)
{
VUE.diagPush("DSV");
if (editInfoDockWindow == null)
initUI();
setLayout(new BorderLayout());
this.DRB = drBrowser;
dataSourceList = new DataSourceList(this);
Widget.setExpanded(DRB.browsePane, false); // working: why expanded sometimes?
loadOSIDDataSources();
loadBrowseableDataSources();
setPopup();
addListeners();
if (true) {
add(dataSourceList);
} else {
// this no good: needs to be done at higher level (DRBrowser, refactored)
JTabbedPane tabs = new JTabbedPane();
//tabs.setBackground(Color.white);
tabs.add(VueResources.getString("jtab.data"), new JLabel(VueResources.getString("jlabel.data")));
tabs.add(VueResources.getString("jtab.resource"), dataSourceList);
add(tabs);
}
// TODO: can any of these (beyond refresh), ever have an effect? These properties
// are being set before addNotify, so I don't think they're generated the needed
// propertyChangeEvents to be seen, and aren't currently being handled in
// the WidgetStack.WidgetTitle constructor
Widget.setHelpAction(DRB.librariesPane,VueResources.getString("dockWindow.Resources.libraryPane.helpText"));
//dockWindow.addButton is not a localized string in this context its just a property that is later resolved.
Widget.setMiscAction(DRB.librariesPane, new MiscActionMouseListener(), "dockWindow.addButton");
// Widget.setHelpAction(DRB.browsePane,VueResources.getString("dockWindow.Resources.browsePane.helpText")); There is no such property and if there was, the question mark would be blue instead of brown.
Widget.setHelpAction(DRB.resultsPane,VueResources.getString("dockWindow.Resources.resultsPane.helpText"));
Widget.setHelpAction(DRB.searchPane,VueResources.getString("dockWindow.Resources.searchPane.helpText"));
Widget.setRefreshAction(DRB.browsePane, new MouseAdapter() {
public void mousePressed(MouseEvent e) {
refreshBrowser();
}
});
VUE.diagPop(); // DSV
editInfoDockWindow.setLocation(DRB.dockWindow.getX() + DRB.dockWindow.getWidth(),
DRB.dockWindow.getY());
NoConfig.setMinimumSize(new Dimension(100,50));
configMetaData.setName(VueResources.getString("datasourceviewer.name.contentdescription"));
Widget.setExpanded(DRB.browsePane, false); // working: why expanded sometimes?
singleton = this;
if (DEBUG.BOXES) setBorder(new LineBorder(Color.red, 4));
}
// private static boolean OSIDsLoaded;
// static void loadOSIDs() {
// if (singleton != null && !OSIDsLoaded)
// singleton.loadOSIDDataSources();
// }
// static void configureOSIDs() {
// VUE.diagPush("config");
// Log.info("configuring data sources");
// edu.tufts.vue.dsm.impl.VueDataSourceManager.getInstance()
// .startRepositoryConfiguration(singleton);
// //UrlAuthentication.getInstance(); // VUE-879
// VUE.diagPop();
// }
// static void configureOSIDs() {
// Log.info("configuring data sources");
// VUE.diagPush("config");
// final edu.tufts.vue.dsm.DataSource dataSources[] =
// edu.tufts.vue.dsm.impl.VueDataSourceManager.getInstance().getDataSources();
// for (final edu.tufts.vue.dsm.DataSource ds : dataSources) {
// if (ds instanceof VueDataSource) {
// Log.debug("configure: " + ds);
// new Thread("CONFIG: " + ds) {
// @Override
// public void run() {
// try {
// ((VueDataSource)ds).assignRepositoryConfiguration();
// } catch (Throwable t) {
// Log.error("configuring;", t);
// //Log.error("configuring: " + ds + ";", t);
// }
// Log.info("CONFIGURED");
// if (singleton != null) {
// // TODO: VueDataSource accessors used during painting (in AWT thread) not fully thread-safe
// // against the above assignRepositoryConfiguration -- we're relying on luck at the moment.
// singleton.repaint();
// } else
// Log.warn("config complete, no UI to update");
// }
// }.start();
// } else {
// Log.info("unknown DataSource, cannot configure: " + Util.tags(ds));
// }
// }
// //UrlAuthentication.getInstance(); // VUE-879
// VUE.diagPop();
// }
private void loadBrowseableDataSources()
{
//org.apache.log4j.NDC.push(getClass().getSimpleName() + ";");
try {
// load old-style data sources
Log.info("Loading old style data sources...");
loadOldStyleDataSources();
Log.info("Loaded old style data sources.");
} catch (Throwable t) {
VueUtil.alert(VueResources.getString("dialog.loadresourceerror.message"),VueResources.getString("dialog.loadresourceerror.title"));
}
}
private void loadOSIDDataSources()
{
edu.tufts.vue.dsm.DataSource dataSources[] = null;
try {
// load new data sources
dataSourceManager = edu.tufts.vue.dsm.impl.VueDataSourceManager.getInstance();
Log.info("requesting installed data sources from VDSM");
// VUE.diagPush("LD");
// edu.tufts.vue.dsm.impl.VueDataSourceManager.load();
// VUE.diagPop();
dataSources = dataSourceManager.getDataSources();
//Log.info("finished loading data sources.");
VUE.diagPush("UI");
for (int i = 0; i < dataSources.length; i++) {
final int index = i;
final edu.tufts.vue.dsm.DataSource ds = dataSources[i];
if (DEBUG.DR) Log.info(" add to UI: " + ds);
dataSourceList.addOrdered(ds);
// GUI.invokeAfterAWT(new Runnable() { public void run() {
// try {
// dataSourceList.addOrdered(ds);
// } catch (Throwable t) {
// Log.error("adding to UI: " + ds, t);
// VueUtil.alert("Error loading Resource #" + (index+1), "Error");
// }
// }});
}
VUE.diagPop();
} catch (Throwable t) {
Log.error(t);
VueUtil.alert(VueResources.getString("dialog.loadosiderror.message")+ "\n" + t, VueResources.getString("dialog.loadosiderror.title"));
}
federatedSearchManager = edu.tufts.vue.fsm.impl.VueFederatedSearchManager.getInstance();
sourcesAndTypesManager = edu.tufts.vue.fsm.impl.VueSourcesAndTypesManager.getInstance();
if (DEBUG.Enabled) Log.debug("sourcesAndTypesManager: " + Util.tags(sourcesAndTypesManager));
// TODO: redesign: loadOSIDDataSources asks for a query editor, which triggers
// the creation of an edu.tufts.vue.ui.DefaultQueryEditor, which asks VDSM for
// included repositories, but now that repositories are not configured until
// later in case of hangs, they often all have a null repository reference, and
// are thus not included when DefaultQueryEditor asks for them. In practice
// this shouldn't currently be hanging us up, as we refresh the query edtior
// later early and often, which re-asks for included repositories, but it
// indicates a circular dependency during initialization that could someday be
// problematic. A delayed or lazy create of the queryEditor reference should fix
// this.
queryEditor = federatedSearchManager.getQueryEditorForType(searchType);
queryEditor.addSearchListener(this);
DRB.searchPane.removeAll();
DRB.searchPane.add((JPanel) queryEditor, DRBrowser.SEARCH_EDITOR);
DRB.searchPane.revalidate();
DRB.searchPane.repaint();
// WORKING: stop using this preview panel?
queryEditor.addSearchListener(VUE.getInspectorPane());
//this.previewPanel = previewDockWindow.getWidgetPanel();
//resultSetDockWindow = DRB.searchDock;
// select the first new data source, if any
if (activeDataSource == null && dataSources != null && dataSources.length > 0 && !VUE.isApplet())
setActiveDataSource(dataSources[0]);
}
class MiscActionMouseListener extends MouseAdapter
{
public void mouseClicked(MouseEvent e)
{
addLibraryAction.actionPerformed(null);
}
}
private void displayContextMenu(MouseEvent e) {
getPopup(e).show(e.getComponent(), e.getX(), e.getY());
}
JPopupMenu m = null;
private static final JMenuItem aboutResource = new JMenuItem(VueResources.getString("datasourcehandler.aboutthisresources"));
//private static final JMenuItem configureResource = new JMenuItem("Configure Resource");
private static final JMenuItem deleteResource = new JMenuItem(VueResources.getString("datasourcehandler.deleteresource"));
Point lastMouseClick = null;
public void actionPerformed(ActionEvent e)
{
if (e.getSource().equals(aboutResource))
{
int index = dataSourceList.locationToIndex(lastMouseClick);
//ResourceIcon o = (ResourceIcon)this.getModel().getElementAt(index);
if (dataSourceList.getModel().getElementAt(index) instanceof DataSource)
{
displayEditOrInfo((DataSource)dataSourceList.getModel().getElementAt(index));
}
else
{
displayEditOrInfo((edu.tufts.vue.dsm.DataSource)dataSourceList.getModel().getElementAt(index));
}
}
/*else if (e.getSource().equals(configureResource))
{
}*/
else if (e.getSource().equals(deleteResource))
{
int index = dataSourceList.locationToIndex(lastMouseClick);
dataSourceList.setSelectedIndex(index);
removeLibraryAction.actionPerformed(e);
}
}
private JPopupMenu getPopup(MouseEvent e)
{
if (m == null)
{
m = new JPopupMenu(VueResources.getString("datasourcehandle.menu.datasource"));
m.add(aboutResource);
m.addSeparator();
//m.add(configureResource);
//m.addSeparator();
m.add(deleteResource);
aboutResource.addActionListener(this);
//configureResource.addActionListener(this);
deleteResource.addActionListener(this);
}
return m;
}
private void addListeners() {
//dataSourceList.addKeyListener(this); // not currently used
// WORKING: commented out
//librariesDockWindow.setVisible(true); // try to make menu appear
dataSourceList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (DEBUG.KEYS || DEBUG.EVENTS) Log.debug("valueChanged: " + e);
Object o = ((JList)e.getSource()).getSelectedValue();
if (o !=null) {
// for the moment, we are doing double work to keep old data sources
if (o instanceof tufts.vue.DataSource) {
DataSource ds = (DataSource)o;
DataSourceViewer.this.setActiveDataSource(ds);
refreshEditInfo(ds);
} else if (o instanceof edu.tufts.vue.dsm.DataSource) {
edu.tufts.vue.dsm.DataSource ds = (edu.tufts.vue.dsm.DataSource)o;
DataSourceViewer.this.setActiveDataSource(ds);
refreshEditInfo(ds);
} else {
int index = ((JList)e.getSource()).getSelectedIndex();
o = dataSourceList.getModelContents().getElementAt(index-1);
if (o instanceof tufts.vue.DataSource) {
DataSource ds = (DataSource)o;
DataSourceViewer.this.setActiveDataSource(ds);
refreshEditInfo(ds);
} else if (o instanceof edu.tufts.vue.dsm.DataSource) {
edu.tufts.vue.dsm.DataSource ds = (edu.tufts.vue.dsm.DataSource)o;
DataSourceViewer.this.setActiveDataSource(ds);
refreshEditInfo(ds);
}
}
}
refreshMenuActions();
}}
);
dataSourceList.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
if (activeDataSource instanceof DataSource) {
displayEditOrInfo((DataSource)activeDataSource);
} else {
displayEditOrInfo((edu.tufts.vue.dsm.DataSource)activeDataSource);
}
} else {
Point pt = e.getPoint();
if ( (activeDataSource instanceof edu.tufts.vue.dsm.DataSource) && (pt.x <= 40) ) {
int index = dataSourceList.locationToIndex(pt);
edu.tufts.vue.dsm.DataSource ds = (edu.tufts.vue.dsm.DataSource)
dataSourceList.getModel().getElementAt(index);
boolean included = !ds.isIncludedInSearch();
if (DEBUG.DR) Log.debug("DataSource " + ds + " [" + ds.getProviderDisplayName() + "] inclusion: " + included);
ds.setIncludedInSearch(included);
dataSourceList.repaint();
//queryEditor.refresh();
GUI.invokeAfterAWT(new Runnable() { public void run() {
queryEditor.refresh();
try {
synchronized (dataSourceManager) {
if (DEBUG.DR) Log.debug("DataSourceManager saving...");
dataSourceManager.save();
if (DEBUG.DR) Log.debug("DataSourceManager saved.");
}
} catch (Throwable t) {
tufts.Util.printStackTrace(t);
}
}});
}
if(e.getButton() == e.BUTTON3)
{
lastMouseClick = e.getPoint();
displayContextMenu(e);
}
//popup.show(e.getComponent(), e.getX(), e.getY());
}
}
});
}
public void setActiveDataSource(edu.tufts.vue.dsm.DataSource ds) {
this.activeDataSource = ds;
dataSourceList.setSelectedValue(ds,true);
Widget.setExpanded(DRB.browsePane, false);
Widget.setExpanded(DRB.searchPane, true);
queryEditor.refresh();
}
public static Object getActiveDataSource() {
return activeDataSource;
}
void expandBrowse() {
Widget.setExpanded(DRB.browsePane, true);
}
void refreshBrowser()
{
if (browserDS == null || browserDS.isLoading())
return;
browserDS.unloadViewer();
dataSourceList.repaint(); // so change in loaded status will be visible
displayInBrowsePane(produceViewer(browserDS), false);
}
protected void displayInBrowsePane(JComponent viewer, boolean priority)
{
if (DEBUG.Enabled) Log.debug("displayInBrowsePane: " + browserDS + "; " + GUI.name(viewer));
String title = VueResources.getString("button.browse.label")+": " + browserDS.getDisplayName();
if (browserDS.getCount() > 0)
title += " (" + browserDS.getCount() + ")";
Widget.setTitle(DRB.browsePane, title);
if (priority)
Widget.setExpanded(DRB.searchPane, false);
DRB.browsePane.removeAll();
DRB.browsePane.add(viewer);
DRB.browsePane.revalidate();
DRB.browsePane.repaint();
Widget.setExpanded(DRB.browsePane, true);
}
protected void repaintList() {
dataSourceList.repaint(); // so change in loaded status will be visible
}
public void setActiveDataSource(final tufts.vue.DataSource ds)
{
//if (DEBUG.Enabled) Log.debug("setActiveDataSource: " + ds, new Throwable("FYI"));
if (DEBUG.Enabled) Log.debug("setActiveDataSource: " + ds);
this.activeDataSource = ds;
this.dataSourceList.setSelectedValue(ds, true);
this.browserDS = (tufts.vue.BrowseDataSource) ds;
displayInBrowsePane(produceViewer(browserDS), true);
}
public static void refreshDataSourcePanel(edu.tufts.vue.dsm.DataSource ds) {
queryEditor.refresh();
//TODO: actually replace the whole editor if need be
}
public void setPopup() {
popup = new JPopupMenu();
checkForUpdatesAction = new AbstractAction(VueResources.getString("datasourcehandler.updateresources")) {
public void actionPerformed(ActionEvent e) {
try {
edu.tufts.vue.dsm.OsidFactory factory = edu.tufts.vue.dsm.impl.VueOsidFactory.getInstance();
org.osid.provider.ProviderIterator providerIterator = factory.getProvidersNeedingUpdate();
if (providerIterator.hasNextProvider()) {
if (updateLibraryDialog == null) {
updateLibraryDialog = new UpdateLibraryDialog(dataSourceList,
((edu.tufts.vue.dsm.DataSource)dataSourceList.getSelectedValue()));
} else {
updateLibraryDialog.refresh();
updateLibraryDialog.setVisible(true);
}
} else VueUtil.alert(VUE.getDialogParent(),
VueResources.getString("dialog.checkforupdatesaction.message"),
VueResources.getString("dialog.checkforupdatesaction.title"),
javax.swing.JOptionPane.INFORMATION_MESSAGE);
} catch (Throwable t) {
VueUtil.alert(t.getMessage(),VueResources.getString("dialog.loadresourceerror.title"));
}
}
};
addLibraryAction = new AbstractAction(VueResources.getString("datasourcehandler.addresources")) {
public void actionPerformed(ActionEvent e) {
try {
// there are always resources that can be added, e.g. a local file system
if (addLibraryDialog == null) {
addLibraryDialog = new AddLibraryDialog(dataSourceList);
} else {
addLibraryDialog.refresh();
addLibraryDialog.setVisible(true);
}
// reflect addition, if any, in UI
DataSource ds = addLibraryDialog.getOldDataSource();
if (ds != null) {
setActiveDataSource(ds);
} else {
edu.tufts.vue.dsm.DataSource ds1 = addLibraryDialog.getNewDataSource();
if (ds1 != null) {
setActiveDataSource(ds1);
}
}
} catch (Throwable t) {
VueUtil.alert(t.getMessage(),VueResources.getString("dialog.loadresourceerror.title"));
}
}
};
editLibraryAction = new AbstractAction(VueResources.getString("datasourcehandler.aboutthisresources")) {
public void actionPerformed(ActionEvent e) {
Object o = dataSourceList.getSelectedValue();
if (o != null) {
// for the moment, we are doing double work to keep old data sources
if (o instanceof edu.tufts.vue.dsm.DataSource) {
edu.tufts.vue.dsm.DataSource ds = (edu.tufts.vue.dsm.DataSource)o;
displayEditOrInfo(ds);
} else {
displayEditOrInfo((DataSource)o);
}
}
}
};
removeLibraryAction = new AbstractAction(VueResources.getString("datasourcehandler.deleteresource")) {
public void actionPerformed(ActionEvent e) {
Object o = dataSourceList.getSelectedValue();
if (o != null) {
// for the moment, we are doing double work to keep old data sources
if (o instanceof edu.tufts.vue.dsm.DataSource) {
edu.tufts.vue.dsm.DataSource ds = (edu.tufts.vue.dsm.DataSource)o;
String displayName = ds.getRepositoryDisplayName();
//figure out which one of the results needs to be deleted
DefaultListModel listComponents = dataSourceList.getModelContents();
int index = dataSourceList.getModelContents().indexOf(ds);
int instanceIndex = 0;
for (int p=0; p< index; p++) {
edu.tufts.vue.dsm.impl.VueDataSource o2 = (edu.tufts.vue.dsm.impl.VueDataSource)listComponents.get(p);
if (o2.getRepositoryDisplayName() == ds.getRepositoryDisplayName())
instanceIndex++;
}
if (VueUtil.confirm(VUE.getDialogParent(),
String.format(Locale.getDefault(), VueResources.getString("datasource.dialog.message"), displayName),
VueResources.getString("datasource.dialog.title"),
javax.swing.JOptionPane.OK_CANCEL_OPTION) == javax.swing.JOptionPane.YES_OPTION) {
dataSourceManager.remove(ds.getId());
GUI.invokeAfterAWT(new Runnable() { public void run() {
try {
synchronized (dataSourceManager) {
if (DEBUG.DR) Log.debug("DataSourceManager saving...");
dataSourceManager.save();
if (DEBUG.DR) Log.debug("DataSourceManager saved.");
}
} catch (Throwable t) {
tufts.Util.printStackTrace(t);
}
}});
dataSourceList.getModelContents().removeElement(ds);
saveDataSourceViewer();
//delete it
WidgetStack widgetStack = null;
//if (DRB.resultsPane != null)
// widgetStack = (WidgetStack)DRB.resultsPane.getComponent(0);
if (widgetStack != null) {
Component[] comps = widgetStack.getComponents();
int found =0;
for (int i = 0; i < comps.length; i++) {
String compName = comps[i].getName();
if ((compName != null) && (compName.indexOf(displayName)!= -1)) {
if ((found == instanceIndex) || (found == instanceIndex+1))
widgetStack.remove(comps[i]);
found++;
}
}
}
}
} else if( o instanceof tufts.vue.DataSource) {
tufts.vue.DataSource ds = (tufts.vue.DataSource) o;
String displayName = ds.getDisplayName();
if (VueUtil.confirm(VUE.getDialogParent(),
String.format(Locale.getDefault(), VueResources.getString("datasource.dialog.message"), displayName),
VueResources.getString("datasource.dialog.title"),
javax.swing.JOptionPane.OK_CANCEL_OPTION) == javax.swing.JOptionPane.YES_OPTION) {
dataSourceList.getModelContents().removeElement(ds);
saveDataSourceViewer();
}
DRB.browsePane.remove(ds.getResourceViewer());
DRB.browsePane.revalidate();
DRB.browsePane.repaint();
}
}
DataSourceViewer.this.popup.setVisible(false);
}
};
refreshMenuActions();
}
private void refreshMenuActions() {
Object o = dataSourceList.getSelectedValue();
edu.tufts.vue.dsm.DataSource ds = null;
if (o != null) {
// for the moment, we are doing double work to keep old data sources
if (o instanceof edu.tufts.vue.dsm.DataSource) {
ds = (edu.tufts.vue.dsm.DataSource)o;
removeLibraryAction.setEnabled(true);
} else if (o instanceof RemoteFileDataSource) {
// FTP
removeLibraryAction.setEnabled(true);
} else {
// My Computer and My Saved Content
removeLibraryAction.setEnabled(true);
}
editLibraryAction.setEnabled(true);
} else {
removeLibraryAction.setEnabled(false);
editLibraryAction.setEnabled(false);
}
checkForUpdatesAction.setEnabled(true);
Widget.setMenuActions(DRB.librariesPane,
new Action[] {
addLibraryAction,
checkForUpdatesAction,
null,
editLibraryAction,
removeLibraryAction
});
}
private boolean checkValidUser(String userName,String password,int type) {
if(type == 3) {
try {
TuftsDLAuthZ tuftsDL = new TuftsDLAuthZ();
osid.shared.Agent user = tuftsDL.authorizeUser(userName,password);
if(user == null)
return false;
if(tuftsDL.isAuthorized(user, TuftsDLAuthZ.AUTH_VIEW))
return true;
else
return false;
} catch(Exception ex) {
VueUtil.alert(null,VueResources.getString("dialog.checkvaliduser.message") +ex, VueResources.getString("dialog.checkvaliduser.title"));
ex.printStackTrace();
return false;
}
} else
return true;
}
private static final Collection<tufts.vue.DataSource> oldStyleDataSources = new ArrayList();
private void loadOldStyleDataSources() {
VUE.diagPush("BDS"); // BrowseDataSource (oldStyle)
boolean init = true;
File f = new File(VueUtil.getDefaultUserFolder().getAbsolutePath()+File.separatorChar+VueResources.getString("save.datasources"));
if (DEBUG.DR) Log.debug("Data source file: " + f.getAbsolutePath());
if (!f.exists()) {
if (DEBUG.DR) System.out.println("Loading default DataSources (does not exist: " + f + ")");
loadDefaultDataSources();
} else {
int type;
try {
if (DEBUG.DR) Log.debug("Loading saved DataSources from " + f + "; unmarshalling...");
VUE.diagPush("XML");
final SaveDataSourceViewer dataSourceContainer = unMarshallMap(f);
VUE.diagPop();
if (DEBUG.DR) Log.debug("Unmarshalling completed from " + f);
final Vector dataSources = dataSourceContainer.getSaveDataSources();
synchronized (oldStyleDataSources) {
oldStyleDataSources.clear();
oldStyleDataSources.addAll(dataSources);
}
if (DEBUG.DR) Log.debug("Found " + dataSources.size() + " DataSources: " + dataSources);
int i = 0;
while (!(dataSources.isEmpty())){
final DataSource ds = (DataSource) dataSources.remove(0);
// Don't load XML/CSV data sources -- those are now loaded in DataSetViewer.java -- but do
// load any others: for example, folders dropped onto the resources tab.
if (!ds.getTypeName().equals(XmlDataSource.TYPE_NAME)) {
i++;
if (DEBUG.DR) Log.debug(String.format("#%02d: loading %s ", i, Util.tags(ds)));
// VUE.diagPush("#" + i);
// ds.setResourceViewer();
// VUE.diagPop();
try {
dataSourceList.addOrdered(ds);
} catch(Exception ex) {System.out.println("DataSourceViewer.loadOldStyleDataSources"+ex);}
}
}
} catch (Exception ex) {
Log.error("Loading DataSources; loading defaults as fallback", ex);
loadDefaultDataSources();
}
}
VUE.diagPop();
}
public static void cacheDataSourceViewers() {
if (singleton != null)
singleton.cacheViewers();
}
private void cacheViewers() {
VUE.diagPush("dsv-cache");
final java.util.List<tufts.vue.DataSource> dataSources;
synchronized (oldStyleDataSources) {
dataSources = new ArrayList(oldStyleDataSources);
}
for (DataSource ds : dataSources) {
Log.info("requesting viewer for: " + Util.tags(ds));
try {
produceViewer((tufts.vue.BrowseDataSource)ds, true);
} catch (Throwable t) {
Log.error("exception caching viewer for " + Util.tags(ds), t);
}
}
VUE.diagPop();
}
private void loadDefaultDataSources() {
try {
String breakTag = "";
//dataSourceList.getModelContents().addElement(breakTag);
DataSource ds1 = new FavoritesDataSource(VueResources.getString("addLibrary.mysavedcontent"));
dataSourceList.addOrdered(ds1);
//dataSourceList.getModelContents().addElement(breakTag);
DataSource ds2 = new LocalFileDataSource(VueResources.getString("addLibrary.mycomputer"),"");
dataSourceList.addOrdered(ds2);
// default selection
dataSourceList.setSelectedValue(ds2,true);
DataSourceViewer.saveDataSourceViewer();
} catch (Exception ex) {
//if(DEBUG.DR) System.out.println("Datasource loading problem ="+ex);
Util.printStackTrace(ex, "Loading default data sources");
}
}
// private ImageIcon getThumbnail(org.osid.repository.Asset asset) {
// try {
// org.osid.repository.RecordIterator recordIterator = asset.getRecords();
// while (recordIterator.hasNextRecord()) {
// org.osid.repository.Record record = recordIterator.nextRecord();
// org.osid.repository.PartIterator partIterator = record.getParts();
// while (partIterator.hasNextPart()) {
// org.osid.repository.Part part = partIterator.nextPart();
// if (part.getPartStructure().getType().isEqual(thumbnailType)) {
// // ImageIcon icon = new ImageIcon(Toolkit.getDefaultToolkit().getImage((String)part.getValue()));
// ImageIcon icon = new ImageIcon(new URL((String)part.getValue()));
// return icon;
// }
// }
// }
// } catch (Throwable t) {
// t.printStackTrace();
// }
// return noImageIcon;
// }
private synchronized void stopAllSearches() {
// if (UseFederatedSearchManager) {
// tufts.Util.printStackTrace("stopAllSearches unimplemented for FSM");
// return;
// }
synchronized (mSearchThreads) {
if (DEBUG.DR) Log.debug("STOPPING ALL ACTIVE SEARCHES; count=" + mSearchThreads.size());
for (Thread t : mSearchThreads)
t.interrupt();
}
synchronized (mMapBasedSearchThreads) {
if (DEBUG.DR) Log.debug("STOPPING ALL ACTIVE SEARCHES; count=" + mSearchThreads.size());
for (Thread t : mMapBasedSearchThreads)
t.interrupt();
}
}
public static Action getAddLibraryAction()
{
return addLibraryAction;
}
public static Action getUpdateLibraryAction()
{
return checkForUpdatesAction;
}
/**
* Kick off map based searching.
* @param c the component we're basing the search on
*/
public void mapBasedSearch(LWComponent c) {
/* if (se == null) {
// null SearchEvent means abort last search
stopAllSearches();
return;
}
*/
DefaultQueryEditor.setStopLabels();
Widget.setExpanded(DRB.browsePane, false);
if (DEBUG.DR) {
try {
System.out.println("\n");
Log.debug("Search includes:");
for (edu.tufts.vue.dsm.DataSource ds : dataSourceManager.getDataSources()) {
System.out.print("\t");
if (ds.isIncludedInSearch()) {
System.out.print("+ ");
} else {
System.out.print("- ");
}
System.out.println(ds + "; Provider=" + ds.getProviderDisplayName());
}
} catch (Throwable t) {
Util.printStackTrace(t, this + "; debug code");
}
}
performParallelSearchesAndMapResults(c);
}
static java.util.List<Resource> listOfLists = null;
private class MapBasedSearchThread extends Thread {
public final Widget mResultPane;
private final org.osid.repository.Repository mRepository;
private final String mSearchString;
private java.io.Serializable mSearchCriteria;
private org.osid.shared.Type mSearchType;
private org.osid.shared.Properties mSearchProperties;
private final StatusLabel mStatusLabel;
private final String mRepositoryName;
private LWComponent mCenterComponent;
public MapBasedSearchThread(org.osid.repository.Repository r,
String searchString,
Serializable searchCriteria,
org.osid.shared.Type searchType,
org.osid.shared.Properties searchProperties,
LWComponent c)
throws org.osid.repository.RepositoryException {
super("Search" + (SearchCounter++) + " " + searchString + " in " + repositoryName(r));
setDaemon(true);
if (listOfLists == null)
listOfLists = Collections.synchronizedList(new ArrayList());
if (DEBUG.DR)
{
System.out.println("Search Thread\n-----------");
System.out.println("Search String : " + searchString);
System.out.println("Search Criteria : " + searchCriteria);
}
mCenterComponent = c;
mRepository = r;
mSearchString = searchString;
mSearchCriteria = searchCriteria;
mSearchType = searchType;
mSearchProperties = searchProperties;
mRepositoryName = repositoryName(r);
//If the naming convention of this were to change, note there would
//need to be a change in WidgetStack to properly color code the widget.
mResultPane = new Widget(VueResources.getString("datasource.dialog.searching")+" "+ mRepositoryName);
mStatusLabel = new StatusLabel(VueResources.getString("resource.dialog.searchingfor")+" " + mSearchString + " ...", false);
mResultPane.add(mStatusLabel);
if (DEBUG.DR) Log.debug("created search thread for: " + mRepositoryName + " \t" + mRepository);
}
public void run() {
if (stopped())
return;
if (DEBUG.DR) Log.debug("RUN KICKED OFF");
// TODO: all the swing access should be happening on the EDT for
// absolute thread safety. Refactor using SwingWorker.
try {
adjustQuery();
if (stopped())
return;
// TODO: ultimately, the repository will need some kind of callback
// architecture, so that a search can be aborted even while waiting for
// the server to come back, tho it'll probably need to use channel based
// NIO to really get that working. Should that day come, the federated
// search manager could handle this full threading and calling us back
// as results come in, so we could skip our threading code here, and so
// other GUI's could take advantage of the fully parallel search code.
// INVOKE THE SEARCH, and immediately hand off to processResultsAndDisplay
AssetIterator asIt = mRepository.getAssetsBySearch(mSearchCriteria, mSearchType, mSearchProperties);
assets.add(asIt);
processMapBasedSearchResultsAndDisplay(asIt);
} catch (Throwable t) {
Util.printStackTrace(t);
GUI.clearWaitCursor();
if (stopped())
return;
final JTextArea textArea;
if (DEBUG.Enabled) {
textArea = new JTextArea(mRepositoryName + ": Search Error: " + t);
} else {
String msg = translateRepositoryException(t.getLocalizedMessage());
textArea = new JTextArea(mRepositoryName + ": Search Error: " + msg);
}
textArea.setBorder(new EmptyBorder(4,22,6,0));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
GUI.apply(GUI.ErrorFace, textArea);
textArea.setOpaque(false);
GUI.invokeAfterAWT(new Runnable() { public void run() {
mResultPane.setTitle(VueResources.getString("searchgui.results") +": "+ mRepositoryName);
mResultPane.removeAll();
mResultPane.add(textArea);
}});
}
if (stopped()) {
GUI.clearWaitCursor();
if (DEBUG.DR) Log.debug("DELAYED STOP; server returned, run completed.");
return;
}
mMapBasedSearchThreads.remove(this);
if (DEBUG.DR) Log.debug("RUN COMPLETED, stillActive=" + mSearchThreads.size());
// must call revalidate because we're coming from another thread:
//mResultPane.revalidate();
if (mMapBasedSearchThreads.size() == 0) {
listOfLists.clear();
GUI.clearWaitCursor();
// If we were stopped, the DefaultQueryEditor will have handled
// calling completeSearch to restore the state of the "Search" button.
if (DEBUG.DR) Log.debug("ALL SEARCHES COMPLETED for \"" + mSearchCriteria + "\"");
if (queryEditor instanceof edu.tufts.vue.ui.DefaultQueryEditor)
((edu.tufts.vue.ui.DefaultQueryEditor)queryEditor).completeSearch();
}
}
private String translateRepositoryException(String msg)
{
String s;
if (msg.equals(org.osid.repository.RepositoryException.OPERATION_FAILED))
{
s = VueResources.getString("repositoryexception.operationfailed");
}
else if (msg.equals(org.osid.repository.RepositoryException.PERMISSION_DENIED))
{
s = VueResources.getString("repositoryexception.permissiondenied");
}
else if (msg.equals(org.osid.repository.RepositoryException.CONFIGURATION_ERROR))
{
s = VueResources.getString("repositoryexception.configurationerror");
}
else
{
s = VueResources.getString("repositoryexception.genericmsg");
}
return s;
}
// As we create a new Widget for the output of every search, in terms of a new
// search replacing a still running search, we're already safe UI wise even if
// we never interrupted a search, but we might as well be careful about it / not
// waste cycles, and it's nice if the user can abort if desired.
private boolean stopped() {
if (isInterrupted()) {
if (DEBUG.DR) Log.debug("STOPPING");
return true;
} else
return false;
}
public void interrupt() {
if (DEBUG.DR) Log.debug("INTERRUPTED " + this);
super.interrupt();
GUI.invokeAfterAWT(new Runnable() { public void run() {
mResultPane.setTitle(mRepositoryName + " (Stopped)");
mStatusLabel.removeIcon();
mStatusLabel.setText(VueResources.getString("datasourcehandle.searchstopped.tooltip"));
}});
}
private void adjustQuery()
throws org.osid.repository.RepositoryException {
//if (DEBUG.DR) Log.debug("checking for query adjustment");
edu.tufts.vue.fsm.QueryAdjuster adjuster = federatedSearchManager
.getQueryAdjusterForRepository(mRepository.getId());
if (adjuster != null) {
edu.tufts.vue.fsm.Query q = adjuster.adjustQuery(mRepository,
mSearchCriteria,
mSearchType,
mSearchProperties);
mSearchCriteria = q.getSearchCriteria();
mSearchType = q.getSearchType();
mSearchProperties = q.getSearchProperties();
if (DEBUG.DR) Log.debug("adjusted query");
}
//if (DEBUG.DR) Log.debug("done checking for query adjustment");
}
private void processMapBasedSearchResultsAndDisplay(org.osid.repository.AssetIterator assetIterator)
throws org.osid.repository.RepositoryException
{
if (stopped())
return;
if (DEBUG.DR) Log.debug("processing AssetIterator: " + Util.tags(assetIterator));
final java.util.List<Resource> resourceList = new java.util.ArrayList<Resource>();
final int maxResult = 10;
int resultCount = 0;
if (assetIterator != null) {
try {
while (assetIterator.hasNextAsset() && (resultCount < maxResult)) {
org.osid.repository.Asset asset = assetIterator.nextAsset();
if (++resultCount > maxResult)
continue;
if (asset == null) {
Log.warn("null asset in " + mRepositoryName + "; " + assetIterator);
continue;
}
try {
resourceList.add(Resource.instance(mRepository, asset, DataSourceViewer.this.context));
} catch (Throwable t) {
Log.warn("Failed to create resource for asset: " + Util.tags(asset), t);
}
}
} catch (Throwable t) {
if (resourceList.size() < 1) {
if (t instanceof RepositoryException)
throw (RepositoryException) t;
else
throw new RuntimeException("processing assets for " + mRepositoryName, t);
} else {
// we have at least one result: dump exception and continue
Util.printStackTrace(t, "processing asset iterator for " + mRepositoryName);
}
}
if (DEBUG.DR) Log.debug("done processing AssetIterator; count=" + resultCount);
}
String name = VueResources.getString("searchgui.results") +": " + mRepositoryName;
if (DEBUG.DR) {
if (resultCount > maxResult)
Log.debug(name + "; returned a total of " + resultCount + " matches");
Log.debug(name + "; " + resourceList.size() + " results");
}
if (resourceList.size() > 0)
name += " (" + resourceList.size() + ")";
if (stopped())
return;
final String title = name;
final JComponent results;
if (resourceList.size() == 0) {
if (assetIterator == null)
results = new StatusLabel("Empty results for " + mSearchString);
else
results = new StatusLabel("No results for " + mSearchString);
} else {
results = new ResourceList(resourceList, title);
}
Iterator<Resource> iterator = listOfLists.iterator();
// System.out.println("AAAAAAA " +listOfLists.size());
while (iterator.hasNext())
{
Resource r = iterator.next();
Iterator<Resource> it = resourceList.iterator();
synchronized(resourceList)
{
while (it.hasNext())
{
Resource re = it.next();
// System.out.println(re.getSpec() + " :::::: " + r.getSpec());
if (re.getSpec().equals(r.getSpec()))
{
// System.out.println("RESULT FILTERED");
it.remove();
}
}
}
}
listOfLists.addAll(resourceList);
// System.out.println("AAAAAAA" + listOfLists.size());
GUI.invokeAfterAWT(new Runnable() { public void run() {
mResultPane.setTitle(title);
mResultPane.removeAll();
mResultPane.add(results);
if (resourceList.size() > 0)
AnalyzerAction.addResultsToMap(resourceList,mCenterComponent,mMapBasedSearchThreads.size());
}});
}
}
/**
*
* @param c The node this search is based on.
*/
private synchronized void performParallelSearchesAndMapResults(LWComponent c)
{
final String searchString = "\"" + queryEditor.getSearchDisplayName() + "\"";
final WidgetStack resultsStack = new WidgetStack("searchResults " + searchString);
final org.osid.repository.Repository[] repositories = sourcesAndTypesManager.getRepositoriesToSearch();
final java.io.Serializable searchCriteria = queryEditor.getCriteria();
final org.osid.shared.Type searchType = queryEditor.getSearchType();
final org.osid.shared.Properties searchProperties = queryEditor.getProperties();
mMapBasedSearchThreads.clear();
if (DEBUG.DR) {
Log.debug("Searching criteria [" + searchString + "] in selected repositories."
+ "\n\tsearchType=" + searchType
+ "\n\tsearchProps=" + searchProperties);
}
for (int i = 0; i < repositories.length; i++) {
final org.osid.repository.Repository repository = repositories[i];
if (repository == null) {
Util.printStackTrace("null repository #" + i + ": skipping search");
continue;
}
MapBasedSearchThread searchThread = null;
try {
searchThread = new MapBasedSearchThread(repository, searchString,
searchCriteria, searchType, searchProperties,c);
} catch (Throwable t) {
Util.printStackTrace(t, "Failed to create search in " + repository);
if (DEBUG.Enabled)
VueUtil.alert(VueResources.getString("dialog.searcherror.message"), t);
else
VueUtil.alert(t.getMessage(), VueResources.getString("dialog.searcherror.message"));
}
mMapBasedSearchThreads.add(searchThread);
resultsStack.addPane(searchThread.mResultPane, 0f);
}
DRB.searchPane.add(resultsStack, DRBrowser.SEARCH_RESULT);
//-----------------------------------------------------------------------------
// KICK OFF THE SEARCH THREADS
//-----------------------------------------------------------------------------
// synchronized (mSearchThreads) {
// for (Thread t : mSearchThreads)
Iterator<MapBasedSearchThread> it = mMapBasedSearchThreads.iterator();
while (it.hasNext())
{
Thread t = it.next();
t.start();
}
}
public void searchPerformed(edu.tufts.vue.fsm.event.SearchEvent se) {
if (se == null) {
// null SearchEvent means abort last search
stopAllSearches();
return;
}
Widget.setExpanded(DRB.browsePane, false);
if (DEBUG.DR) {
try {
System.out.println("\n");
Log.debug("Search includes:");
for (edu.tufts.vue.dsm.DataSource ds : dataSourceManager.getDataSources()) {
System.out.print("\t");
if (ds.isIncludedInSearch()) {
System.out.print("+ ");
} else {
System.out.print("- ");
}
System.out.println(ds + "; Provider=" + ds.getProviderDisplayName());
}
} catch (Throwable t) {
Util.printStackTrace(t, this + "; debug code");
}
}
performParallelSearchesAndDisplayResults();
// if (UseFederatedSearchManager) {
// new Thread("VUE-Search") {
// public void run() {
// if (DEBUG.DR || DEBUG.THREAD) Log.debug("search thread kicked off");
// try {
// performFederatedSearchAndDisplayResults();
// } catch (Throwable t) {
// tufts.Util.printStackTrace(t);
// if (DEBUG.Enabled)
// VueUtil.alert("Search Error", t);
// else
// VueUtil.alert(t.getMessage(), "Search Error");
// } finally {
// queryEditor.refresh();
// }
// }
// }.start();
// } else {
// performParallelSearchesAndDisplayResults();
// }
}
private static JLabel SearchingLabel;
private static final boolean UseSingleScrollPane = true;
public static class StatusLabel extends JPanel {
private static final Icon SlowSpinningIcon = VueResources.getIcon("dsv.statuspanel.waitIcon");
private final JLabel label;
private final JComponent waitIcon;
public StatusLabel(String s, boolean center, boolean useIcon) {
super();
if (center) {
setLayout(new FlowLayout(FlowLayout.CENTER));
setBorder(new EmptyBorder(3,0,3,0));
} else {
setLayout(new FlowLayout(FlowLayout.LEFT));
setBorder(new EmptyBorder(3,10,3,0));
}
//setBackground(VueResources.getColor("dsv.statuspanel.bgColor"));
setBackground(SystemColor.control);
if (useIcon) {
if (Util.isMacLeopard()) {
final JProgressBar spinner = new JProgressBar();
spinner.setIndeterminate(true);
spinner.putClientProperty("JProgressBar.style", "circular");
waitIcon = spinner;
} else {
waitIcon = new JLabel(SlowSpinningIcon);
}
this.add(waitIcon);
} else
waitIcon = null;
label = new JLabel(s);
GUI.apply(GUI.StatusFace, label);
if (!useIcon && Util.isMacLeopard()) {
// match icon width & height so text is aligned with labels that have icons
//label.setBorder(GUI.makeSpace(1,23,1,0));
// match icon height so search box doesn't change height when "no results" comes back
label.setBorder(GUI.makeSpace(1,0,1,0));
}
this.add(label);
//setMinimumSize(new Dimension(getWidth(), WidgetStack.TitleHeight+14));
//setPreferredSize(new Dimension(getWidth(), WidgetStack.TitleHeight+14));
}
StatusLabel(String s, boolean center) { this(s,center,true); }
StatusLabel(String s) { this(s,false,false); }
public void removeIcon() {
if (waitIcon != null)
remove(waitIcon);
}
public void setText(String s) {
label.setText(s);
}
}
//private static final String UNKNOWN_REPOSITORY_NAME = "Unknown Repository Name";
private static String repositoryName(org.osid.repository.Repository r) {
if (r == null)
return "Unknown Repository";
try {
String s = r.getDisplayName();
if (s == null || s.trim().length() < 1) {
// if (r.getId() != null)
// return "Unknown Name: Repository " + r.getId().getIdString();
// else
return "Unknown Repository Name";
} else
return s;
} catch (Throwable t) {
Util.printStackTrace(t);
return "[RepositoryName: " + t + "]";
}
}
private static int SearchCounter = 0;
private class SearchThread extends Thread {
public final Widget mResultPane;
private final org.osid.repository.Repository mRepository;
private final String mSearchString;
private java.io.Serializable mSearchCriteria;
private org.osid.shared.Type mSearchType;
private org.osid.shared.Properties mSearchProperties;
private final StatusLabel mStatusLabel;
private final String mRepositoryName;
public SearchThread(org.osid.repository.Repository r,
String searchString,
Serializable searchCriteria,
org.osid.shared.Type searchType,
org.osid.shared.Properties searchProperties)
throws org.osid.repository.RepositoryException {
super("Search" + (SearchCounter++) + " " + searchString + " in " + repositoryName(r));
setDaemon(true);
if (DEBUG.DR)
{
System.out.println("Search Thread\n-----------");
System.out.println("Search String : " + searchString);
System.out.println("Search Criteria : " + searchCriteria);
}
mRepository = r;
mSearchString = searchString;
mSearchCriteria = searchCriteria;
mSearchType = searchType;
mSearchProperties = searchProperties;
mRepositoryName = repositoryName(r);
//If the naming convention of this were to change, note there would
//need to be a change in WidgetStack to properly color code the widget.
mResultPane = new Widget(VueResources.getString("datasource.dialog.searching")+" "+ mRepositoryName);
mStatusLabel = new StatusLabel(VueResources.getString("resource.dialog.searchingfor")+" "+ mSearchString + " ...", false);
mResultPane.add(mStatusLabel);
if (DEBUG.DR) Log.debug("created search thread for: " + mRepositoryName + " \t" + mRepository);
}
public void run() {
if (stopped())
return;
if (DEBUG.DR) Log.debug("RUN KICKED OFF");
// TODO: all the swing access should be happening on the EDT for
// absolute thread safety. Refactor using SwingWorker.
try {
adjustQuery();
if (stopped())
return;
// TODO: ultimately, the repository will need some kind of callback
// architecture, so that a search can be aborted even while waiting for
// the server to come back, tho it'll probably need to use channel based
// NIO to really get that working. Should that day come, the federated
// search manager could handle this full threading and calling us back
// as results come in, so we could skip our threading code here, and so
// other GUI's could take advantage of the fully parallel search code.
// INVOKE THE SEARCH, and immediately hand off to processResultsAndDisplay
processResultsAndDisplay(mRepository.getAssetsBySearch(mSearchCriteria, mSearchType, mSearchProperties));
} catch (Throwable t) {
Util.printStackTrace(t);
if (stopped())
return;
final JTextArea textArea;
if (DEBUG.Enabled) {
textArea = new JTextArea(mRepositoryName + ": Search Error: " + t);
} else {
String msg = translateRepositoryException(t.getLocalizedMessage());
textArea = new JTextArea(mRepositoryName + ": Search Error: " + msg);
}
textArea.setBorder(new EmptyBorder(4,22,6,0));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
GUI.apply(GUI.ErrorFace, textArea);
textArea.setOpaque(false);
GUI.invokeAfterAWT(new Runnable() { public void run() {
mResultPane.setTitle(VueResources.getString("searchgui.results")+": " + mRepositoryName);
mResultPane.removeAll();
mResultPane.add(textArea);
}});
}
if (stopped()) {
if (DEBUG.DR) Log.debug("DELAYED STOP; server returned, run completed.");
return;
}
mSearchThreads.remove(this);
if (DEBUG.DR) Log.debug("RUN COMPLETED, stillActive=" + mSearchThreads.size());
// must call revalidate because we're coming from another thread:
//mResultPane.revalidate();
if (mSearchThreads.size() == 0) {
// If we were stopped, the DefaultQueryEditor will have handled
// calling completeSearch to restore the state of the "Search" button.
if (DEBUG.DR) Log.debug("ALL SEARCHES COMPLETED for \"" + mSearchCriteria + "\"");
if (queryEditor instanceof edu.tufts.vue.ui.DefaultQueryEditor)
((edu.tufts.vue.ui.DefaultQueryEditor)queryEditor).completeSearch();
}
}
private String translateRepositoryException(String msg)
{
String s;
if (msg.equals(org.osid.repository.RepositoryException.OPERATION_FAILED))
{
s = VueResources.getString("repositoryexception.operationfailed");
}
else if (msg.equals(org.osid.repository.RepositoryException.PERMISSION_DENIED))
{
s = VueResources.getString("repositoryexception.permissiondenied");
}
else if (msg.equals(org.osid.repository.RepositoryException.CONFIGURATION_ERROR))
{
s = VueResources.getString("repositoryexception.configurationerror");
}
else
{
s = VueResources.getString("repositoryexception.genericmsg");
}
return s;
}
// As we create a new Widget for the output of every search, in terms of a new
// search replacing a still running search, we're already safe UI wise even if
// we never interrupted a search, but we might as well be careful about it / not
// waste cycles, and it's nice if the user can abort if desired.
private boolean stopped() {
if (isInterrupted()) {
if (DEBUG.DR) Log.debug("STOPPING");
return true;
} else
return false;
}
public void interrupt() {
if (DEBUG.DR) Log.debug("INTERRUPTED " + this);
super.interrupt();
GUI.invokeAfterAWT(new Runnable() { public void run() {
mResultPane.setTitle(mRepositoryName + " (Stopped)");
mStatusLabel.removeIcon();
mStatusLabel.setText(VueResources.getString("datasourcehandle.searchstopped.tooltip"));
}});
}
private void adjustQuery()
throws org.osid.repository.RepositoryException {
//if (DEBUG.DR) Log.debug("checking for query adjustment");
edu.tufts.vue.fsm.QueryAdjuster adjuster = federatedSearchManager
.getQueryAdjusterForRepository(mRepository.getId());
if (adjuster != null) {
edu.tufts.vue.fsm.Query q = adjuster.adjustQuery(mRepository,
mSearchCriteria,
mSearchType,
mSearchProperties);
mSearchCriteria = q.getSearchCriteria();
mSearchType = q.getSearchType();
mSearchProperties = q.getSearchProperties();
if (DEBUG.DR) Log.debug("adjusted query");
}
//if (DEBUG.DR) Log.debug("done checking for query adjustment");
}
private void processResultsAndDisplay(org.osid.repository.AssetIterator assetIterator)
throws org.osid.repository.RepositoryException
{
if (stopped())
return;
if (DEBUG.DR) Log.debug("processing AssetIterator: " + Util.tags(assetIterator));
final java.util.List resourceList = new java.util.ArrayList();
final int maxResult = 500;
int resultCount = 0;
if (assetIterator != null) {
try {
while (assetIterator.hasNextAsset() && (resultCount < maxResult)) {
org.osid.repository.Asset asset = assetIterator.nextAsset();
if (++resultCount > maxResult)
continue;
if (asset == null) {
Log.warn("null asset in " + mRepositoryName + "; " + assetIterator);
continue;
}
try {
resourceList.add(Resource.instance(mRepository, asset, DataSourceViewer.this.context));
} catch (Throwable t) {
Log.warn("Failed to create resource for asset: " + Util.tags(asset), t);
}
}
} catch (Throwable t) {
if (resourceList.size() < 1) {
if (t instanceof RepositoryException)
throw (RepositoryException) t;
else
throw new RuntimeException("processing assets for " + mRepositoryName, t);
} else {
// we have at least one result: dump exception and continue
Util.printStackTrace(t, "processing asset iterator for " + mRepositoryName);
}
}
if (DEBUG.DR) Log.debug("done processing AssetIterator; count=" + resultCount);
}
String name = VueResources.getString("searchgui.results")+": " + mRepositoryName;
if (DEBUG.DR) {
if (resultCount > maxResult)
Log.debug(name + "; returned a total of " + resultCount + " matches");
Log.debug(name + "; " + resourceList.size() + " results");
}
if (resourceList.size() > 0)
name += " (" + resourceList.size() + ")";
if (stopped())
return;
final String title = name;
final JComponent results;
if (resourceList.size() == 0) {
if (assetIterator == null)
results = new StatusLabel("Empty results for " + mSearchString);
else
results = new StatusLabel("No results for " + mSearchString);
} else {
results = new ResourceList(resourceList, title);
}
GUI.invokeAfterAWT(new Runnable() { public void run() {
mResultPane.setTitle(title);
mResultPane.removeAll();
mResultPane.add(results);
}});
}
}
private synchronized void performParallelSearchesAndDisplayResults()
{
// if (DEBUG.DR) {
// synchronized (System.out) {
// System.out.println("Current search threads:");
// for (Thread t : mSearchThreads)
// System.out.println("\t" + t);
// //mSearchThreadGroup.list();
// }
// }
// //mSearchThreadGroup.interrupt();
// todo minor bug: if search string is too long, it can be left out of the UI
// pending results area entirely while the search is running (we just see the spinner)
final String searchString = "\"" + queryEditor.getSearchDisplayName() + "\"";
final WidgetStack resultsStack = new WidgetStack("searchResults " + searchString);
final org.osid.repository.Repository[] repositories = sourcesAndTypesManager.getRepositoriesToSearch();
final java.io.Serializable searchCriteria = queryEditor.getCriteria();
final org.osid.shared.Type searchType = queryEditor.getSearchType();
final org.osid.shared.Properties searchProperties = queryEditor.getProperties();
mSearchThreads.clear();
if (DEBUG.DR) {
Log.debug("Searching criteria [" + searchString + "] in selected repositories."
+ "\n\tsearchType=" + searchType
+ "\n\tsearchProps=" + searchProperties);
}
for (int i = 0; i < repositories.length; i++) {
final org.osid.repository.Repository repository = repositories[i];
if (repository == null) {
Util.printStackTrace("null repository #" + i + ": skipping search");
continue;
}
SearchThread searchThread = null;
try {
searchThread = new SearchThread(repository, searchString,
searchCriteria, searchType, searchProperties);
} catch (Throwable t) {
Util.printStackTrace(t, "Failed to create search in " + repository);
if (DEBUG.Enabled)
VueUtil.alert(VueResources.getString("dialog.searcherror.message"), t);
else
VueUtil.alert(t.getMessage(), VueResources.getString("dialog.searcherror.message"));
}
mSearchThreads.add(searchThread);
resultsStack.addPane(searchThread.mResultPane, 0f);
}
DRB.searchPane.add(resultsStack, DRBrowser.SEARCH_RESULT);
//-----------------------------------------------------------------------------
// KICK OFF THE SEARCH THREADS
//-----------------------------------------------------------------------------
synchronized (mSearchThreads) {
for (Thread t : mSearchThreads)
t.start();
}
}
// private synchronized void performFederatedSearchAndDisplayResults()
// throws org.osid.repository.RepositoryException,
// org.osid.shared.SharedException {
// //final String dockTitle = "Search Results for \"" + queryEditor.getSearchDisplayName() + "\"";
// final String searchString = "\"" + queryEditor.getSearchDisplayName() + "\"";
// /*
// Store our results since we will fill a panel with each repository's results and one with all.
// We can't get the iterator contents again, without re-doing the search.
// We know the repositories we searched. Some may have returned results, others may not. We will
// make a vector for each set of results with a parallel vector of repository ids.
// */
// final java.util.List resultList = new java.util.ArrayList();
// final java.util.List dataSourceIdStringList = new java.util.ArrayList();
// final java.util.List repositoryDisplayNameList = new java.util.ArrayList();
// org.osid.repository.Repository[] repositories = sourcesAndTypesManager.getRepositoriesToSearch();
// edu.tufts.vue.dsm.DataSource[] dataSources = sourcesAndTypesManager.getDataSourcesToSearch(); // will be same length
// final WidgetStack resultsStack = new WidgetStack("searchResults " + searchString);
// final Widget[] resultPanes = new Widget[repositories.length];
// for (int i = 0; i < repositories.length; i++) {
// org.osid.repository.Repository r = repositories[i];
// if (DEBUG.DR) Log.debug("to search: " + r.getDisplayName() + " \t" + r);
// dataSourceIdStringList.add(dataSources[i].getId().getIdString());
// repositoryDisplayNameList.add(r.getDisplayName());
// resultList.add(new java.util.ArrayList());
// resultPanes[i] = new Widget("Searching " + r.getDisplayName());
// resultPanes[i].add(new StatusLabel("Searching for " + searchString + " ...", true));
// resultsStack.addPane(resultPanes[i], 0f);
// }
// DRB.searchPane.add(resultsStack, DRBrowser.SEARCH_RESULT);
// // get our search results
// java.io.Serializable searchCriteria = queryEditor.getCriteria();
// if (DEBUG.DR) {
// Log.debug("Searching criteria [" + searchCriteria + "] in selected repositories. SearchProps=" + queryEditor.getProperties());
// }
// org.osid.shared.Properties searchProperties = queryEditor.getProperties();
// edu.tufts.vue.fsm.ResultSetManager resultSetManager
// = federatedSearchManager.getResultSetManager(searchCriteria,
// queryEditor.getSearchType(),
// searchProperties);
// if (DEBUG.DR) Log.debug("got result set manager " + resultSetManager);
// for (int i=0; i < dataSources.length; i++) {
// org.osid.repository.AssetIterator assetIterator = resultSetManager.getAssets(dataSources[i].getId().getIdString());
// int counter = 0;
// while (assetIterator.hasNextAsset() && (counter <= 100)) {
// org.osid.repository.Asset nextAsset = assetIterator.nextAsset();
// counter++;
// String dataSourceIdString = dataSources[i].getId().getIdString();
// int index = dataSourceIdStringList.indexOf(dataSourceIdString);
// java.util.List v = (java.util.List) resultList.get(index);
// // TODO: Resources eventually want to be atomic, so a factory
// // should be queried for a resource based on the asset.
// Osid2AssetResource resource = new Osid2AssetResource(nextAsset, this.context);
// v.add(resource);
// }
// }
// // Display the results in the result panes
// for (int i = 0; i < repositories.length; i++) {
// java.util.List resourceList = (java.util.List) resultList.get(i);
// String name = "Results: " + (String) repositoryDisplayNameList.get(i);
// if (DEBUG.DR) Log.debug(name + ": " + resourceList.size() + " results");
// if (resourceList.size() > 0)
// name += " (" + resourceList.size() + ")";
// resultPanes[i].setTitle(name);
// resultPanes[i].removeAll();
// if (resourceList.size() == 0) {
// //resultsStack.addPane(name, new JLabel(" No results"), 0f);
// // there might have been an exception
// String message = resultSetManager.getExceptionMessage(i);
// if (message != null) {
// resultPanes[i].add(new StatusLabel(message, false));
// } else {
// resultPanes[i].add(new StatusLabel("No results for " + searchString, false));
// }
// } else {
// resultPanes[i].add(new ResourceList(resourceList));
// }
// }
// }
static PropertyMap buildPropertyMap(final edu.tufts.vue.dsm.DataSource ds) {
final PropertyMap map = new PropertyMap();
try {
final org.osid.repository.Repository r = ds.getRepository();
if (r != null) {
map.addProperty("Repository Id", r.getId().getIdString());
map.addProperty("Name", r.getDisplayName());
map.addProperty("Description", r.getDescription());
map.addProperty("Type", edu.tufts.vue.util.Utilities.typeToString(r.getType()));
} else {
map.addProperty("Repository Id (DS)", ds.getRepositoryId());
}
map.addProperty("Creator", ds.getCreator());
map.addProperty("Publisher", ds.getPublisher());
map.addProperty("Release Date", edu.tufts.vue.util.Utilities.dateToString(ds.getReleaseDate()));
map.addProperty("Provider Id", ds.getProviderId().getIdString());
String osidName = ds.getOsidName() + " " + ds.getOsidVersion();
map.addProperty("Osid Service", osidName);
map.addProperty("Osid Load Key", ds.getOsidLoadKey());
map.addProperty("Provider Display Name", ds.getProviderDisplayName());
map.addProperty("Provider Description", ds.getProviderDescription());
map.addProperty("Online?", ds.isOnline() ? "Yes" : "No");
String supportsUpd = ds.supportsUpdate() ? "The Resource Supports Updating" : "The Resource Is Read Only";
map.addProperty("Supports Update?", supportsUpd);
if (r != null) {
org.osid.shared.TypeIterator typeIterator = r.getAssetTypes();
final StringBuilder assetTypes = new StringBuilder();
if (typeIterator.hasNextType())
assetTypes.append(edu.tufts.vue.util.Utilities.typeToString(typeIterator.nextType()));
while (typeIterator.hasNextType()) {
assetTypes.append(", ");
assetTypes.append(edu.tufts.vue.util.Utilities.typeToString(typeIterator.nextType()));
}
map.addProperty("Asset Types", assetTypes.toString());
typeIterator = r.getSearchTypes();
final StringBuilder searchTypes = new StringBuilder();
if (typeIterator.hasNextType())
searchTypes.append(edu.tufts.vue.util.Utilities.typeToString(typeIterator.nextType()));
while (typeIterator.hasNextType()) {
searchTypes.append(", ");
searchTypes.append(edu.tufts.vue.util.Utilities.typeToString(typeIterator.nextType()));
}
map.addProperty("Search Types", searchTypes.toString());
}
/* java.awt.Image image = null;
if ( (image = dataSource.getIcon16x16()) != null ) {
gbConstraints.gridx = 0;
gbConstraints.gridy++;
add(new javax.swing.JLabel(new javax.swing.ImageIcon(image)),gbConstraints);
}*/
} catch (Throwable t) {
//t.printStackTrace();
//System.out.println(t.getMessage());
Log.warn("buildPropertyMap", t);
}
return map;
}
private static WidgetStack editInfoStack; // static hack: is needed before this class is constructed
private static final JLabel NoConfig = new JLabel(VueResources.getString("jlabel.noconfig"), JLabel.CENTER);
private final MetaDataPane configMetaData = new MetaDataPane("Config Properties", true);
private Object loadedDataSource;
static void initUI() {
editInfoDockWindow = buildConfigWindow();
}
private static DockWindow buildConfigWindow() {
try {
return _buildWindow();
} catch (Throwable t) {
Log.error("buildConfigWindow", t);
}
return null;
}
private void positionEditInfoWindow()
{
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
if ((DRB.dockWindow.getX() + DRB.dockWindow.getWidth() + editInfoDockWindow.getWidth()) < screenSize.getWidth())
editInfoDockWindow.setLocation(DRB.dockWindow.getX() + DRB.dockWindow.getWidth(),
DRB.dockWindow.getY());
else
editInfoDockWindow.setLocation(DRB.dockWindow.getX() - editInfoDockWindow.getWidth(),
DRB.dockWindow.getY());
}
private void displayEditOrInfo(edu.tufts.vue.dsm.DataSource ds) {
if (DEBUG.DR) Log.debug("DISPLAY " + Util.tags(ds));
if (!editInfoDockWindow.isVisible())
positionEditInfoWindow();
refreshEditInfo(ds, true);
editInfoDockWindow.setVisible(true);
editInfoDockWindow.raise();
}
private void displayEditOrInfo(DataSource ds) {
if (DEBUG.DR) Log.debug("DISPLAY " + Util.tags(ds));
if (!editInfoDockWindow.isVisible())
positionEditInfoWindow();
refreshEditInfo(ds, true);
editInfoDockWindow.setVisible(true);
editInfoDockWindow.raise();
}
// TODO: Dock title always "Resource: name", Configuration widget title
// always "Configuration: <type>", e.g., OSID, LocalFileDataSource, RSSDataSource, etc.
private static DockWindow _buildWindow() {
final DockWindow dw = GUI.createDockWindow(VueResources.getString("dockWindow.resource.title"));
editInfoStack = new WidgetStack();
//editInfoStack.addPane("startup", new javax.swing.JLabel("config init"));
editInfoStack.setMinimumSize(new Dimension(300,300));
dw.setContent(editInfoStack);
if (DEBUG.Enabled) {
//editInfoStack.setMinimumSize(new Dimension(400,600));
dw.setSize(500,800);
} else {
dw.setWidth(300);
dw.setHeight(500);
}
// We don't have DRB yet to set location.
return dw;
}
private void refreshEditInfo(edu.tufts.vue.dsm.DataSource ds) {
refreshEditInfo(ds, false);
}
private void refreshEditInfo(tufts.vue.DataSource ds) {
refreshEditInfo(ds, false);
}
private void doLoad(Object dataSource, String name) {
editInfoStack.setTitleItem(name);
//editInfoDockWindow.invalidate();
//editInfoDockWindow.repaint();
loadedDataSource = dataSource;
}
private void refreshEditInfo(edu.tufts.vue.dsm.DataSource ds, boolean force) {
if (ds == loadedDataSource)
return;
if (DEBUG.DR && DEBUG.META) Log.debug("refresh " + Util.tags(ds));
if (force || editInfoDockWindow.isVisible()) {
if (DEBUG.DR) Log.debug("REFRESH " + Util.tags(ds));
editInfoStack.removeAll();
final String name;
if (DEBUG.Enabled)
//name = "Configuration: " + ds.getClass().getName(); // always edu.tufts.vue.dsm.impl.VueDataSource
name = VueResources.getString("optiondialog.configuration.message")+": " + ds.getRepository();
else
name = VueResources.getString("optiondialog.configuration.message");
if (ds.hasConfiguration()) {
editInfoStack.addPane(name, new EditLibraryPanel(this, ds));
} else {
editInfoStack.addPane(name, NoConfig);
if (DEBUG.Enabled) {
;
} else {
Widget.setExpanded(NoConfig, false);
}
}
final PropertyMap dsProps = buildPropertyMap(ds);
configMetaData.loadTable(dsProps);
editInfoStack.addPane(configMetaData, 2.0f);
doLoad(ds, ds.getRepositoryDisplayName());
//This is for Adding default veritcal expander
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
//c.anchor = GridBagConstraints.NORTH;
c.weighty = 0.01;
JLabel label = new JLabel(VueResources.getString("jlabel.widgetstack"),JLabel.CENTER);
if (DEBUG.BOXES) {
dummyPanel.setOpaque(true);
dummyPanel.setBackground(Color.darkGray);
dummyPanel.setForeground(Color.white);
dummyPanel.setMinimumSize(new Dimension(0,0));
//label.setPreferredSize(new Dimension(0,0));
dummyPanel.add(label);
}
dummyPanel.setSize(0,0);
dummyPanel.setMinimumSize(new Dimension(0,0));
dummyPanel.setPreferredSize(new Dimension(0,0));
editInfoStack.add(dummyPanel,c);
}
}
private void refreshEditInfo(tufts.vue.DataSource ds, boolean force) {
if (ds == loadedDataSource)
return;
if (DEBUG.DR && DEBUG.META) Log.debug("refresh " + Util.tags(ds));
if (force || editInfoDockWindow.isVisible()) {
if (DEBUG.DR) Log.debug("REFRESH " + Util.tags(ds));
editInfoStack.removeAll();
final String name;
if (DEBUG.Enabled)
name = VueResources.getString("optiondialog.configuration.message")+": " + ds.getClass().getName();
else
name = VueResources.getString("optiondialog.configuration.message")+": " + ds.getTypeName();
editInfoStack.addPane(name, new EditLibraryPanel(this, ds), 1f);
doLoad(ds, ds.getDisplayName());
}
}
/**
* static method that returns all the datasource where Maps can be published.
* Only FEDORA @ Tufts is available at present
*/
public static Vector getPublishableDataSources(int i) {
Vector mDataSources = new Vector();
if (dataSourceList != null) {
Enumeration e = dataSourceList.getModelContents().elements();
while(e.hasMoreElements() ) {
Object mDataSource = e.nextElement();
if(mDataSource instanceof Publishable)
mDataSources.add(mDataSource);
}
}
return mDataSources;
}
/**
* returns the default favorites resources. This is will be used to add favorites and perform search
*/
public static org.osid.repository.Repository getDefualtFavoritesRepository() {
DefaultListModel model = dataSourceList.getModelContents();
try {
for(int i = 0; i<model.size();i++){
Object o = model.getElementAt(i);
if(o instanceof edu.tufts.vue.dsm.DataSource){
edu.tufts.vue.dsm.DataSource datasource = (edu.tufts.vue.dsm.DataSource)o;
org.osid.repository.Repository repository = datasource.getRepository();
if (repository == null) {
Log.warn("looking for default favorites: repository unavailable in: " + Util.tags(o));
} else if (repository.getType().isEqual(favoritesRepositoryType)) {
return repository;
}
}
}
} catch(Throwable t) {
Log.error("searching " + model, t);
}
return null;
}
public static FavoritesDataSource getDefualtFavoritesDS() {
DefaultListModel model = dataSourceList.getModelContents();
try {
for(int i = 0; i<model.size();i++){
Object o = model.getElementAt(i);
if(o instanceof FavoritesDataSource){
return (FavoritesDataSource)o;
}
}
} catch(Throwable t) {
t.printStackTrace();
}
return null;
}
public static void saveDataSourceViewer() {
DataSetViewer.saveDataSetViewer();
/* This is now done in DataSetViewer
if (dataSourceList == null) {
System.err.println("DataSourceViewer: No dataSourceList to save.");
return;
}
int size = dataSourceList.getModel().getSize();
File f = new File(VueUtil.getDefaultUserFolder().getAbsolutePath()+File.separatorChar+VueResources.getString("save.datasources"));
Vector sDataSources = new Vector();
if (DEBUG.DR) Log.debug("saveDataSourceViewer: found " + size + " dataSources: scanning for local's to save...");
for (int i = 0; i<size; i++) {
Object item = dataSourceList.getModel().getElementAt(i);
if (DEBUG.DR) System.err.print("\tsaveDataSourceViewer: item " + i + " is " + tufts.Util.tag(item) + "[" + item + "]...");
if (item instanceof DataSource) {
sDataSources.add((DataSource)item);
if (DEBUG.DR) System.err.println("saving");
} else {
if (DEBUG.DR) System.err.println("skipping");
}
}
try {
if (DEBUG.DR) Log.debug("saveDataSourceViewer: creating new SaveDataSourceViewer");
SaveDataSourceViewer sViewer= new SaveDataSourceViewer(sDataSources);
if (DEBUG.DR) Log.debug("saveDataSourceViewer: marshallMap: saving " + sViewer + " to " + f);
marshallMap(f,sViewer);
if (DEBUG.DR) Log.debug("saveDataSourceViewer: saved");
} catch (Throwable t) {
t.printStackTrace();
}
*/
}
public void keyPressed(KeyEvent e) {
if (DEBUG.KEYS) Log.debug(e);
// if (e.isShiftDown() && activeDataSource != null) {
// final int dir;
// if (e.getKeyCode() == KeyEvent.VK_UP)
// dir = -1;
// else if (e.getKeyCode() == KeyEvent.VK_DOWN)
// dir = 1;
// else
// return;
// // todo: not very useful! Need to change the model in the VueDataSourceManager,
// // so this change is persistent. Handle as part of eventually doing away
// // with DataSourceList (using a JList), and just using JComponents.
// final DefaultListModel model = (DefaultListModel) dataSourceList.getModel();
// final int index = model.indexOf(activeDataSource);
// Log.debug("RELOCATING " + activeDataSource + " " + dir);
// final int newIndex = index + dir;
// if (newIndex > 0 && newIndex < model.getSize()) {
// model.removeElementAt(index);
// model.insertElementAt(activeDataSource, newIndex);
// if (dir > 0)
// dataSourceList.setSelectedIndex(index);
// }
// }
}
public void keyReleased(KeyEvent e) {
if (DEBUG.KEYS) Log.debug(e);
}
public void keyTyped(KeyEvent e) {
if (DEBUG.KEYS) Log.debug(e);
}
}