/*
* Copyright 2003-2008 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.Util;
import tufts.vue.gui.GUI;
import tufts.vue.gui.VueButton;
import tufts.vue.gui.Widget;
import tufts.vue.ui.MetaDataPane;
import tufts.vue.ui.ResourceList;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Locale;
import java.util.Vector;
import java.io.*;
import java.util.*;
import java.net.URL;
import edu.tufts.vue.dsm.impl.VueDataSource;
import org.osid.repository.Repository;
import org.osid.repository.RepositoryException;
// castor classes
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.ValidationException;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.osid.provider.ProviderException;
import org.xml.sax.InputSource;
import tufts.vue.gui.*;
import edu.tufts.vue.dsm.impl.VueDataSourceManager;
/**
* 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).
*/
// TODO: in order to keep backward compat with old DataSourceViewer code (unless we're
// ready to just throw the swtich), we'll need an interface that both DataSourceViewer
// and DataSourceHandler can implement, that includes at least a setActiveDataSource for
// the two types, plus addOrdered / getModelContents, or some new pairs of clearear and
// more symmetrical calls that allows adding/remove items from the list.
public class DataSourceHandler extends JPanel
implements edu.tufts.vue.dsm.DataSourceListener,
edu.tufts.vue.fsm.event.SearchListener,
KeyListener,
ActionListener
{
private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(DataSourceHandler.class);
//private static final boolean UseFederatedSearchManager = false;
private final DataFinder DRB;
private Object activeDataSource;
private tufts.vue.BrowseDataSource browserDS;
public final static int ADD_MODE = 0;
public final static int EDIT_MODE = 1;
public final static org.osid.shared.Type favoritesRepositoryType = new edu.tufts.vue.util.Type("edu.tufts","favorites","Favorites");
private JPopupMenu popup;
private static AddLibraryDialog addLibraryDialog;
private static UpdateLibraryDialog updateLibraryDialog;
private static AbstractAction checkForUpdatesAction; // can these really be static?
private static AbstractAction addLibraryAction;
private AbstractAction editLibraryAction; // should these be static?
private AbstractAction removeLibraryAction;
//public static DataSourceList dataSourceList;
public BasicSourcesList dataSourceList;
private static DockWindow editInfoDockWindow; // hack for now: need this set before DSV is created
private edu.tufts.vue.dsm.DataSourceManager dataSourceManager;
private edu.tufts.vue.fsm.FederatedSearchManager federatedSearchManager;
private edu.tufts.vue.fsm.QueryEditor queryEditor;
private edu.tufts.vue.fsm.SourcesAndTypesManager sourcesAndTypesManager;
private final org.osid.shared.Type searchType = new edu.tufts.vue.util.Type("mit.edu","search","keyword");
private final org.osid.shared.Type thumbnailType = new edu.tufts.vue.util.Type("mit.edu","partStructure","thumbnail");
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 static DataSourceHandler singleton;
private class BasicSourcesList extends JList {
//private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(BasicSourcesList.class);
public BasicSourcesList() {
super(new DefaultListModel());
this.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
this.setFixedCellHeight(-1);
this.setCellRenderer(new DataSourceListCellRenderer());
}
public DefaultListModel getModelContents() {
return (DefaultListModel) getModel();
}
// todo: does not maintain groupings on later OSID add update should list contain both OSIDs & VUE local DataSources
public boolean addOrdered(Object o) {
if (excludedSources.contains(o.getClass()))
return false;
if (includedSources.size() > 0 && !includedSources.contains(o.getClass()))
return false;
final DefaultListModel model = getModelContents();
if (!model.contains(o)) {
model.addElement(o);
return true;
} else {
return false;
}
}
}
private final Set<Class> includedSources = new HashSet();
private final Set<Class> excludedSources = new HashSet();
public DataSourceHandler(DataFinder dataFinder, Class[] included, Class[] excluded)
{
if (included != null)
includedSources.addAll(Arrays.asList(included));
if (excluded != null)
excludedSources.addAll(Arrays.asList(excluded));
VUE.diagPush("DSV");
if (editInfoDockWindow == null)
initUI();
setLayout(new BorderLayout());
this.DRB = dataFinder;
dataSourceList = new BasicSourcesList();
//dataSourceList = new DataSourceList(this);
Widget.setExpanded(DRB.browsePane, false); // working: why expanded sometimes?
loadOSIDSearch();
loadOSIDDataSources();
loadBrowseableDataSources();
setPopup();
addListeners();
add(dataSourceList);
// Widget.setRefreshAction(DRB.browsePane, new MouseAdapter() {
// public void mousePressed(MouseEvent e) {
// refreshBrowser();
// }
// });
VUE.diagPop(); // DSV
NoConfig.setMinimumSize(new Dimension(100,50));
configMetaData.setName(VueResources.getString("datasourceviewer.name.contentdescription"));
Widget.setExpanded(DRB.browsePane, false); // working: why expanded sometimes?
// if (singleton == null)
// singleton = this;
if (DEBUG.BOXES) setBorder(new LineBorder(Color.red, 4));
edu.tufts.vue.dsm.impl.VueDataSourceManager.getInstance().addDataSourceListener(this);
}
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"));
}
}
public void changed(final edu.tufts.vue.dsm.DataSource[] dataSources,
final Object state,
final edu.tufts.vue.dsm.DataSource changed)
{
if (state == VueDataSourceManager.DS_CONFIGURED) {
Log.info("loading configured " + changed);
dataSourceList.addOrdered(changed);
}
// else if (state == VueDataSourceManager.DS_ADDED) {
// dataSourceList.addOrdered(changed);
// }
repaint();
}
private void loadOSIDDataSources()
{
//if (true) return;
//OSIDsLoaded = true;
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 via Data Source Manager");
// VUE.diagPush("LD");
// // TODO: THIS NEEDS TO BE GLOBALLY STATIC: WE ONLY WANT TO LOAD ONCE! Is
// // now happening in multiple instances of DataSourceHandler, plus old
// // DataSourceViewer. We should remove this call entirely (make private in
// // VDSM), and have VDSM.getDataSources() call load() the first time.
// edu.tufts.vue.dsm.impl.VueDataSourceManager.load();
// VUE.diagPop();
dataSources = dataSourceManager.getDataSources();
Log.info("got data sources; n=" + dataSources.length);
VUE.diagPush("UI");
for (int i = 0; i < dataSources.length; i++) {
final int index = i;
final edu.tufts.vue.dsm.DataSource ds = dataSources[i];
Log.info(String.format("@%x: adding to the UI: %s", System.identityHashCode(this), 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.loadresourceerror.title"));
}
// select the first new data source, if any
if (activeDataSource == null && dataSources != null && dataSources.length > 0)
setActiveDataSource(dataSources[0]);
}
private void loadOSIDSearch()
{
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));
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;
}
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("datasourcehandle.menu.aboutresource"));
//private static final JMenuItem configureResource = new JMenuItem("Configure Resource");
private static final JMenuItem deleteResource = new JMenuItem(VueResources.getString("datasourcehandle.menu.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);
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);
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);
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);
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());
}
}
});
}
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 Object getActiveDataSource() {
return activeDataSource;
}
tufts.vue.DataSource getBrowsedDataSource() {
return browserDS;
}
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);
}
private void displayInBrowsePane(JComponent viewer, boolean priority)
{
if (DEBUG.Enabled) Log.debug("displayInBrowsePane: " + browserDS + "; " + GUI.name(viewer));
String title = "Browse: " + 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);
}
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);
}
private static final JLabel StatusLabel = new JLabel(VueResources.getString("addLibrary.loading.label"), JLabel.CENTER);
private static final JComponent Status;
static {
GUI.apply(GUI.StatusFace, StatusLabel);
StatusLabel.setAlignmentX(0.5f);
JProgressBar bar = new JProgressBar();
bar.setIndeterminate(true);
if (false && Util.isMacLeopard()) {
bar.putClientProperty("JProgressBar.style", "circular");
bar.setBorder(BorderFactory.createLineBorder(Color.darkGray));
//bar.putClientProperty("JComponent.sizeVariant", "small"); // no effect
//bar.setString("Loading...");// no effect on mac
//bar.setStringPainted(true); // no effect on mac
} else {
if (DEBUG.BOXES) bar.setBorder(BorderFactory.createLineBorder(Color.green));
bar.setBackground(Color.red);
bar.setEnabled(false); // don't make so garish (mostly for mac)
}
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
if (DEBUG.BOXES) StatusLabel.setBorder(BorderFactory.createLineBorder(Color.blue, 1));
panel.add(StatusLabel);
panel.add(Box.createVerticalStrut(5));
panel.add(bar);
panel.setBorder(GUI.WidgetInsetBorder3);
Status = panel;
}
private static int vCount = 0;
private JComponent produceViewer(final tufts.vue.BrowseDataSource ds) {
return produceViewer(ds, false);
}
private static String statusName(tufts.vue.BrowseDataSource ds) {
String s = ds.getAddressName();
if (s == null)
s = ds.getDisplayName();
if (s == null)
s = ds.getTypeName();
return s;
}
private JComponent produceViewer(final tufts.vue.BrowseDataSource ds, final boolean caching) {
if (!SwingUtilities.isEventDispatchThread())
throw new Error("not threadsafe except for AWT");
if (DEBUG.DR) Log.debug("produceViewer: " + ds);
final JComponent viewer = ds.getResourceViewer();
if (viewer != null)
return viewer;
StatusLabel.setText(statusName(ds));
if (ds.isLoading()) {
// could up priority any time we come back through
//ds.getLoadThread().setPriority(Thread.MAX_PRIORITY);
//ds.getLoadThread().setPriority(Thread.NORM_PRIORITY);
return Status;
}
String s = ds.getClass().getSimpleName() + "[" + ds.getDisplayName();
if (ds.getAddressName() != null)
s += "; " + ds.getAddressName();
final String name = s + "]";
final Thread buildViewerThread =
new Thread(String.format("VBLD-%02d %s", vCount++, name)) {
{
setDaemon(true);
if (caching)
setPriority(Thread.currentThread().getPriority() - 1);
}
@Override
public void run() {
if (DEBUG.DR) Log.debug("kicked off");
final JComponent newViewer = buildViewer(ds);
if (isInterrupted()) {
if (DEBUG.DR && newViewer != null)
Log.debug("produced; but not needed: aborting");
return;
}
Log.info("produced " + GUI.name(newViewer));
GUI.invokeAfterAWT(new AWTAcceptViewerTask(ds, this, newViewer, name));
}
};
ds.setLoadThread(buildViewerThread);
buildViewerThread.start();
return Status;
}
private class AWTAcceptViewerTask implements Runnable {
final tufts.vue.BrowseDataSource ds;
final Thread serviceThread;
final JComponent newViewer;
final String name;
AWTAcceptViewerTask(BrowseDataSource ds, Thread serviceThread, JComponent viewer, String name) {
this.ds = ds;
this.serviceThread = serviceThread;
this.newViewer = viewer;
this.name = name;
}
public void run() {
if (serviceThread.isInterrupted()) {
// never possible? we're now synchronous in AWT
Log.warn(name + "; in AWT; but viewer no longer needed: aborting result for " + serviceThread, new Throwable("FYI"));
return;
}
VUE.diagPush(name);
if (DEBUG.Enabled) Log.debug("accepting viewer & setting into VueDataSource");
ds.setViewer(newViewer); // important to do this in AWT; it's why we have this task
// The viewer we've just set may actually be just a text pane
// describing an error condition: now set the actual availablity
// of the content:
ds.setAvailable(newViewer instanceof ErrorText == false);
dataSourceList.repaint(); // so change in loaded status will be visible
//if (DataSourceViewer.this.browserDS == ds) { // important to check this in AWT;
if (browserDS == ds) { // important to check this in AWT;
if (DEBUG.Enabled) Log.debug("currently displayed data-source wants this viewer; displaying");
displayInBrowsePane(newViewer, false); // important to do this in AWT;
}
else
if (DEBUG.DR) Log.debug("display: skipping; user looking at something else");
// this would always fallback-interrupt our own serviceThread but by now it
// has already exited is waiting to die, as the last thing it does is add
// this task to the AWT event queue. There should be no code in the run
// after the invoke. We check for isAlive in setLoadThread just in case,
// before we fallback-interrupt.
// important to do both the get/set in AWT:
if (ds.getLoadThread() == serviceThread)
ds.setLoadThread(null);
VUE.diagPop();
}
}
private static final class ErrorText extends JTextArea {
ErrorText(String txt) {
super(txt);
setEditable(false);
setLineWrap(true);
setWrapStyleWord(true);
setBorder(GUI.WidgetInsetBorder3);
GUI.apply(GUI.StatusFace, this);
//setOpaque(false);
//GUI.apply(GUI.ErrorFace, this);
}
}
/**
*
* @return either the successfully created viewer, or an as informative as possible
* error report panel should we encounter any exceptions. The idea is that this
* method is guaranteed not to return null: always something meaninful to display.
* With one exception: if the thread this is running on has it's interrupted status
* set, it may return null.
*
*/
private JComponent buildViewer(final tufts.vue.BrowseDataSource ds)
{
final String address = ds.getAddress();
JComponent viewer = null;
Throwable exception = null;
try {
viewer = ds.buildResourceViewer();
} catch (Throwable t) {
exception = t;
}
if (Thread.currentThread().isInterrupted()) {
if (DEBUG.DR) Log.debug("built; but not needed: aborting");
return null;
}
if (exception == null && viewer == null)
exception = new Exception("no viewer available");
if (exception != null) {
final Throwable t = exception;
Log.error(ds + "; getResourceViewer:", t);
String txt;
txt = ds.getTypeName() + " unavailable:";
if (t instanceof DataSourceException) {
if (t.getMessage() != null)
txt += " " + t.getMessage();
} else
txt += "\n\nError: " + prettyException(t);
if (t.getCause() != null) {
Throwable c = t.getCause();
Log.error("FULL CAUSE:", c);
txt += "\n\nCause: " + prettyException(c);
}
String a = address;
if (a != null) {
//if (a.length() == 0 || Character.isWhitespace(a.charAt(0)) || Character.isWhitespace(a.charAt(a.length()-1)))
a = '[' + a + ']';
}
txt += "\n\nConfiguration address: " + a;
if (DEBUG.Enabled)
txt += "\n\nDataSource: " + ds.getClass().getName();
txt += "\n\nThis could be a problem with the configuration for this "
+ ds.getTypeName()
+ ", with the local network connection, or with a remote server.";
if (DEBUG.Enabled)
txt += "\n\n" + Thread.currentThread();
viewer = new ErrorText(txt);
}
return viewer;
}
private String prettyException(Throwable t) {
String txt;
if (t.getClass().getName().startsWith("java"))
txt = t.getClass().getSimpleName();
else
txt = t.getClass().getName();
if (t.getMessage() != null)
txt += ": " + t.getMessage();
return txt;
}
// 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) {
Log.error(new Throwable("UNIMPLEMENTED"));
//updateLibraryDialog = new UpdateLibraryDialog(null, 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) {
Log.error(new Throwable("UNIMPLEMENTED"));
//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(),
VueResources.getString("dialog.confirmdelete.resource") + displayName + "?",
VueResources.getString("dialog.deleteresource.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);
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.librariesPanel,
Widget.setMenuActions(DRB.sourcesPane,
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);
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.loadDataSources"+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("My Saved Content");
dataSourceList.addOrdered(ds1);
//dataSourceList.getModelContents().addElement(breakTag);
DataSource ds2 = new LocalFileDataSource("My Computer","");
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();
}
}
public static Action getAddLibraryAction()
{
return addLibraryAction;
}
public static Action getUpdateLibraryAction()
{
return checkForUpdatesAction;
}
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;
private static class StatusLabel extends JPanel {
private JLabel label = null;
private JLabel waitIcon = null;
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"));
if (useIcon) {
waitIcon = new JLabel(VueResources.getImageIcon("dsv.statuspanel.waitIcon"));
this.add(waitIcon);
}
label = new JLabel(s);
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);
}
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);
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("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("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 = 10000;
int resultCount = 0;
if (assetIterator != null) {
try {
while (assetIterator.hasNextAsset()) {
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));
resourceList.add(Resource.instance(mRepository, asset, 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 = "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, false, false);
else
results = new StatusLabel("No results for " + mSearchString, false, false);
} 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();
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));
// }
// }
// }
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;
}
@Override
public void addNotify() {
Log.debug("addNotify");
super.addNotify();
// Window w = SwingUtilities.getWindowAncestor(this);
// editInfoDockWindow.setLocation(w.getX() + w.getWidth(),
// w.getY());
positionEditInfoWindow();
}
private void positionEditInfoWindow()
{
final Window w = SwingUtilities.getWindowAncestor(this);
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
if ((w.getX() + w.getWidth() + editInfoDockWindow.getWidth()) < screenSize.getWidth())
editInfoDockWindow.setLocation(w.getX() + w.getWidth(),
w.getY());
else
editInfoDockWindow.setLocation(w.getX() - editInfoDockWindow.getWidth(),
w.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));
editInfoStack.addPane(name, new JLabel("UNIMPLEMENTED: EditLibraryPanel " + ds.getClass()));
} else {
editInfoStack.addPane(name, NoConfig);
if (DEBUG.Enabled) {
;
} else {
Widget.setExpanded(NoConfig, false);
}
}
final PropertyMap dsProps = DataSourceViewer.buildPropertyMap(ds);
configMetaData.loadTable(dsProps);
editInfoStack.addPane(configMetaData, 1f);
doLoad(ds, ds.getRepositoryDisplayName());
}
}
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);
editInfoStack.addPane(name, new JLabel("UNIMPLEMENTED: EditLibraryPanel " + ds.getClass()));
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;
// 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() {
Log.error(new Throwable("SAVE DISABLED -- NEED TO MAKE SURE LISTS ARE JOINED TO SAVE"));
// 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 static void marshallMap(File file,SaveDataSourceViewer dataSourceViewer) {
Marshaller marshaller = null;
try {
FileWriter writer = new FileWriter(file);
marshaller = new Marshaller(writer);
marshaller.setMapping(tufts.vue.action.ActionUtil.getDefaultMapping());
if (DEBUG.DR) Log.debug("marshallMap: marshalling " + dataSourceViewer + " to " + file + "...");
marshaller.marshal(dataSourceViewer);
if (DEBUG.DR) Log.debug("marshallMap: done marshalling.");
writer.flush();
writer.close();
} catch (Throwable t) {
t.printStackTrace();
System.err.println("DRBrowser.marshallMap " + t.getMessage());
}
}
public SaveDataSourceViewer unMarshallMap(File file)
throws java.io.IOException,
org.exolab.castor.xml.MarshalException,
org.exolab.castor.mapping.MappingException,
org.exolab.castor.xml.ValidationException {
Unmarshaller unmarshaller = tufts.vue.action.ActionUtil.getDefaultUnmarshaller(file.toString());
FileReader reader = new FileReader(file);
SaveDataSourceViewer sviewer = (SaveDataSourceViewer) unmarshaller.unmarshal(new InputSource(reader));
reader.close();
return sviewer;
}
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);
}
}