001/*
002 * Copyright (c) 2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-08-24 22:44:14 +0000 (Mon, 24 Aug 2015) $' 
007 * '$Revision: 33630 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.gui;
031
032import java.awt.BorderLayout;
033import java.awt.Component;
034import java.awt.Cursor;
035import java.awt.event.ActionEvent;
036import java.awt.event.ActionListener;
037import java.awt.event.MouseListener;
038import java.io.File;
039import java.net.URL;
040import java.util.Iterator;
041
042import javax.swing.DefaultListCellRenderer;
043import javax.swing.ImageIcon;
044import javax.swing.JComboBox;
045import javax.swing.JLabel;
046import javax.swing.JList;
047import javax.swing.JOptionPane;
048import javax.swing.JPanel;
049import javax.swing.JTree;
050import javax.swing.event.TreeSelectionListener;
051import javax.swing.tree.TreeSelectionModel;
052
053import org.apache.commons.logging.Log;
054import org.apache.commons.logging.LogFactory;
055import org.kepler.authentication.AuthenticationException;
056import org.kepler.build.modules.Module;
057import org.kepler.build.modules.ModuleTree;
058import org.kepler.gui.popups.LibraryPopupListener;
059import org.kepler.objectmanager.cache.LocalRepositoryManager;
060import org.kepler.objectmanager.library.LibraryManager;
061import org.kepler.objectmanager.repository.RepositoryException;
062import org.kepler.sms.OntologyCatalog;
063import org.kepler.util.StaticResources;
064import org.kepler.util.StatusNotifier;
065
066import ptolemy.actor.gui.TableauFrame;
067import ptolemy.kernel.util.Attribute;
068import ptolemy.kernel.util.IllegalActionException;
069import ptolemy.kernel.util.NameDuplicationException;
070import ptolemy.kernel.util.NamedObj;
071import ptolemy.kernel.util.StringAttribute;
072import ptolemy.moml.EntityLibrary;
073import ptolemy.util.MessageHandler;
074import ptolemy.vergil.tree.EntityTreeModel;
075
076public class ComponentLibraryTab extends JPanel implements TabPane {
077        private static final Log log = LogFactory.getLog(ComponentLibraryTab.class
078                        .getName());
079        private static final boolean isDebugging = log.isDebugEnabled();
080
081        private String _name;
082        protected EntityTreeModel _libraryModel;
083        // protected LibraryManager _lman = LibraryManager.getInstance();
084        /**
085         * The configuration for this tableau.
086         */
087        private TableauFrame _frame;
088
089        protected AnnotatedPTree _library;
090        protected ResultTreeRebuilder _libraryPanel;
091        protected SimpleLibrarySearcher _librarySearcher; // SimpleLibrarySearcher
092        private SimpleSearchUIPane _librarySearchPane; // SimpleSearchUIPane
093        private SearchButtonActionHandler _searchButtonHandler = new SearchButtonActionHandler();
094
095        private JComboBox _ontSelectBox;
096        private OntSelectActionHandler _ontSelectHandler = new OntSelectActionHandler();
097
098        private ImageIcon _ontOpenIcon;
099        private ImageIcon _ontClosedIcon;
100        private ImageIcon _folOpenIcon;
101        private ImageIcon _folClosedIcon;
102
103        private MouseListener _mouseListener;
104        private LibraryActionHandler _libraryActionHandler;
105
106        // XXX currently always doing authenticated queries and gets.
107        // The main ramification here is this means the user will be
108        // prompted on their first search to authenticate. Previously
109        // we only ever did non-authenticated search on Components tab.
110        public static final boolean AUTHENTICATE = true;
111
112        public final String allOntologiesName = "All Ontologies and Folders";
113
114        private LocalRepositoryManager _localRepoManager;
115
116        public ComponentLibraryTab(TableauFrame parent) {
117                super();
118                this.setBackground(TabManager.BGCOLOR);
119                _frame = parent;
120
121                URL ontOpenURL = ComponentLibraryTab.class.getResource(StaticResources
122                                .getSettingsString("ONTOLOGY_TREEICON_OPEN_PATH", ""));
123                if (ontOpenURL != null) {
124                        _ontOpenIcon = new ImageIcon(ontOpenURL);
125                }
126
127                URL ontClosedURL = ComponentLibraryTab.class
128                                .getResource(StaticResources.getSettingsString(
129                                                "ONTOLOGY_TREEICON_CLOSED_PATH", ""));
130                if (ontClosedURL != null) {
131                        _ontClosedIcon = new ImageIcon(ontClosedURL);
132                }
133
134                URL folOpenURL = ComponentLibraryTab.class.getResource(StaticResources
135                                .getSettingsString("FOLDER_TREEICON_OPEN_PATH", ""));
136                if (folOpenURL != null) {
137                        _folOpenIcon = new ImageIcon(folOpenURL);
138                }
139
140                URL folClosedURL = ComponentLibraryTab.class
141                                .getResource(StaticResources.getSettingsString(
142                                                "FOLDER_TREEICON_CLOSED_PATH", ""));
143                if (folClosedURL != null) {
144                        _folClosedIcon = new ImageIcon(folClosedURL);
145                }
146
147                _localRepoManager = LocalRepositoryManager.getInstance();
148
149        }
150
151        /**
152         * Implementation of TabPane getName()
153         */
154        public String getTabName() {
155                return _name;
156        }
157
158        public void setTabName(String name) {
159                _name = name;
160        }
161
162        /**
163         * Implementation of TabPane setParentFrame(TableauFrame)
164         */
165        public void setParentFrame(TableauFrame parent) {
166                if (parent == null) {
167                        //System.out.println("setParentFrame(null)");
168                        closing();
169                } else {
170                        _frame = parent;
171                }
172        }
173
174        private void closing() {
175                // System.out.println("ComponentLibraryTab.closing()");
176                //_mouseListener.closing();
177                _library.removeMouseListener(_mouseListener);
178                _mouseListener = null;
179                //_library.removeListener(_libraryActionHandler);
180                _libraryActionHandler = null;
181                if (_librarySearchPane instanceof SimpleSearchUIPane) {
182                        SimpleSearchUIPane pane = (SimpleSearchUIPane) _librarySearchPane;
183                        pane.closing();
184                }
185                _frame = null;
186
187        }
188
189        /**
190         * Implementation of getParentFrame getName()
191         */
192        public TableauFrame getParentFrame() {
193                return _frame;
194        }
195
196        /**
197         * Set true if want not to display remote ontology
198         */
199        public void setSkipReposiotryOntology(boolean skipRepositoryOntology) {
200                _librarySearcher.setSkipReposiotryOntology(skipRepositoryOntology);
201        }
202
203        /**
204         * Implementation of TabPane getName()
205         */
206        public void initializeTab() {
207
208                StatusNotifier.log("Initializing Actor Tree.");
209                
210                initLibraryTree();
211                this.setBackground(TabManager.BGCOLOR);
212
213                /*
214                 * Getting rid of pluggable Areas. Can get similar results by extending
215                 * this class and using the TabPane extension point.
216                 */
217                
218                StatusNotifier.log("Initializing Library Search.");
219                initLibrarySearchPane();
220                initLibrarySearcher();
221                
222                StatusNotifier.log("Initializing Ontology Selector.");
223                initOntSelectBox();
224                
225                StatusNotifier.log("Initializing Library Panel.");
226                initLibraryPanel();
227
228                JPanel kludgePane = new JPanel();
229                kludgePane.setLayout(new BorderLayout());
230
231                // create and add the search panel to the library
232                this.setLayout(new BorderLayout());
233                this.add(_librarySearchPane, BorderLayout.NORTH);
234                kludgePane.add(_ontSelectBox, BorderLayout.NORTH);
235                kludgePane.add(_libraryPanel, BorderLayout.CENTER);
236                this.add(kludgePane, BorderLayout.CENTER);
237
238        }
239
240        /*
241         * Init the library tree for library panel.
242         */
243        protected void initLibraryTree() {
244                // get the tree model from the LibraryIndex
245                LibraryManager libraryManager = LibraryManager.getInstance();
246
247                _libraryModel = libraryManager.getTreeModel(getFilterFile());
248                _library = new AnnotatedPTree(_libraryModel, this);
249
250                _mouseListener = new LibraryPopupListener(_library);
251                _library.setMouseListener(_mouseListener);
252
253                _library.initAnotatedPTree();
254
255                libraryManager.addLibraryJTree((JTree) _library);
256
257                _libraryActionHandler = new LibraryActionHandler();
258                _library.addListener(_libraryActionHandler);
259                _library.setRootVisible(false);
260        }
261
262        /**
263         * Set the selection of the JTree (result) in this panel to single
264         * selection, and add a selection listener to it.
265         * 
266         * @param listener
267         */
268        public void addTreeSingleSelectionListener(TreeSelectionListener listener) {
269                if (_library != null) {
270                        _library.getSelectionModel().setSelectionMode(
271                                        TreeSelectionModel.SINGLE_TREE_SELECTION);
272                        _library.addTreeSelectionListener(listener);
273                }
274                if (_libraryPanel != null) {
275                        _libraryPanel.setSingleTreeSelectionListener(listener);
276                }
277        }
278
279        /**
280         * Get the result tree after searching.
281         * 
282         * @return null if _libraryPanel is null
283         */
284        public JTree getResultTree() {
285                if (_libraryPanel != null) {
286                        JTree tree = _libraryPanel.getResultTree();
287                        if (tree != null) {
288                                return tree;
289                        } else {
290                                return _library;
291                        }
292                } else if (_library != null) {
293                        return _library;
294                } else {
295                        return null;
296                }
297        }
298
299        protected File getFilterFile() {
300                TableauFrame tableauFrame = this._frame;
301                Attribute moduleAttribute = tableauFrame.getTableau().getAttribute(
302                                "_filterModule");
303                Attribute relativePathAttribute = tableauFrame.getTableau()
304                                .getAttribute("_filterRelativePath");
305                if (moduleAttribute == null || relativePathAttribute == null) {
306                        return null;
307                } else {
308                        try {
309                                String moduleName = ((StringAttribute) moduleAttribute)
310                                                .getExpression();
311                                String relativePath = ((StringAttribute) relativePathAttribute)
312                                                .getExpression();
313                                // NOTE: Should this be getModuleByStemName? Be sure to test on
314                                // installation. Won't matter at the moment because it's only
315                                // used in sensor-view module.
316                                Module module = ModuleTree.instance().getModule(moduleName);
317                                File filterFile = new File(module.getDir(), relativePath);
318                                if (filterFile.exists()) {
319                                        return filterFile;
320                                } else {
321                                        return null;
322                                }
323                        } catch (ClassCastException ex) {
324                                System.err
325                                                .println("Unable to cast attributes _filterModule and _filterRelativePath to StringAttribute");
326                                return null;
327                        }
328                }
329        }
330
331        public void reinitializeTab() {
332                this.removeAll();
333                this.initializeTab();
334                this.updateUI();
335                this.repaint();
336        }
337
338        /*
339         * org.ecoinformatics.seek.ecogrid.SimpleLibrarySearchGUIPane
340         * org.kepler.gui.SimpleSearchUIPane
341         */
342        protected void initLibrarySearchPane() {
343
344                _librarySearchPane = new SimpleSearchUIPane(_searchButtonHandler);
345                if (_librarySearchPane == null) {
346                        System.out.println("error: _searchPane is null.  " + "This "
347                                        + "problem can be fixed by adding a librarySearchGUIPane "
348                                        + "property in the configuration.xml file.");
349                }
350
351        }
352
353        /*
354         * org.ecoinformatics.seek.ecogrid.SimpleLibrarySearcher
355         * org.kepler.gui.SimpleChoiceLibrarySearcher
356         * org.kepler.gui.SimpleLibrarySearcher Other possible search techniques can
357         * be substituted here. this would include the ontological lookup scheme
358         * that requires sparrow support
359         */
360        protected void initLibrarySearcher() {
361
362                _librarySearcher = new SimpleLibrarySearcher(_library,
363                                _librarySearchPane);
364                if (_librarySearcher == null) {
365                        System.out
366                                        .println("error: librarySearcher is null.  This "
367                                                        + "problem can be fixed by adding a librarySearcherFactory "
368                                                        + "property in the configuration.xml file.");
369                }
370
371        }
372
373        protected void initOntSelectBox() {
374
375                OntologyCatalog oc = OntologyCatalog.instance();
376                _ontSelectBox = new JComboBox();
377                _ontSelectBox.addItem(allOntologiesName);
378                Iterator<String> onts = oc.getLibraryOntologyNames();
379                while (onts.hasNext()) {
380                        _ontSelectBox.addItem(onts.next());
381                }
382                _ontSelectBox.addActionListener(_ontSelectHandler);
383                _ontSelectBox.setRenderer(new IconListRenderer());
384
385                for (String l : _localRepoManager.getLocalRepositories().values()) {
386                        _ontSelectBox.addItem(l);
387                }
388
389                this.add(_ontSelectBox, BorderLayout.NORTH);
390
391        }
392
393        /*
394         * org.kepler.gui.ResultTreeRebuilder
395         * org.ecoinformatics.seek.ecogrid.ResultHighlighter
396         * org.ecoinformatics.seek.ecogrid.ResultsLister
397         */
398        protected void initLibraryPanel() {
399
400                try {
401                        _libraryPanel = new ResultTreeRebuilder(_library, null);
402                        if (_libraryPanel == null) {
403                                System.out.println("error: _libraryPanel is null.  ");
404                        }
405                } catch (IllegalActionException iae) {
406                        System.out.println("Error displaying search results: " + iae);
407                }
408
409        }
410
411        public JTree getJTree() {
412                return _library;
413        }
414
415        public class IconListRenderer extends DefaultListCellRenderer {
416
417                public Component getListCellRendererComponent(JList list, Object value,
418                                int index, boolean isSelected, boolean cellHasFocus) {
419
420                        JLabel label = (JLabel) super.getListCellRendererComponent(list,
421                                        value, index, isSelected, cellHasFocus);
422
423                        if (label.getText().equals(allOntologiesName)) {
424                                // No Icon
425                        } else if (_localRepoManager.isLocalRepositoryName(label.getText())) {
426                                label.setIcon(_folClosedIcon);
427                        } else {
428                                label.setIcon(_ontClosedIcon);
429                        }
430                        return label;
431
432                }
433        }
434
435        /**
436         * class to handle the search button
437         * 
438         * @author berkley
439         * @since February 17, 2005
440         */
441        public class SearchButtonActionHandler implements ActionListener {
442                /**
443                 * Description of the Method
444                 * 
445                 * @param e
446                 *            Description of the Parameter
447                 */
448                public void actionPerformed(ActionEvent e) {
449                        
450                        if (isDebugging) {
451                                log.debug("SearchButtonActionHandler.actionPerformed(ActionEvent)");
452                        }
453                        
454                        Component component = null;
455                        
456                        try {
457                                /*
458                                 * Wait cursor is not set when hitting enter in text field.
459                                 * http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5581
460                                 * 
461                                 * However this doesn't help:
462                                 * 
463                                if (e.getSource() != null){
464                                        component = ((Component)e.getSource()).getParent();
465                                        while (component != null){
466                                                component = component.getParent();
467                                                if (component instanceof ComponentLibraryTab){
468                                                        component.requestFocus();
469                                                        component.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
470                                                        break;
471                                                }
472                                        }
473                                }
474                                */
475                                
476                                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
477
478                                // need to disable the action listener here so update() isn't
479                                // called twice
480                                _ontSelectBox.removeActionListener(_ontSelectHandler);
481                                _ontSelectBox.setSelectedItem(allOntologiesName);
482                                _ontSelectBox.addActionListener(_ontSelectHandler);
483
484                                String searchTerm = _librarySearchPane.getSearchTerm();
485                                if (isDebugging)
486                                        log.debug("Search term is '" + searchTerm + "'");
487                                Long totalSearchTime = System.currentTimeMillis();
488                                Long searchTime = System.currentTimeMillis();
489                                LibrarySearchResults searchResults = _librarySearcher.search(
490                                                searchTerm, AUTHENTICATE);
491                                if (isDebugging)
492                                        log.debug("Library search completed in "
493                                                        + (System.currentTimeMillis() - searchTime) + "ms");
494                                searchTime = System.currentTimeMillis();
495                                _libraryPanel.update(searchResults);
496                                if (isDebugging)
497                                        log.debug("Library updated in "
498                                                        + (System.currentTimeMillis() - searchTime) + "ms");
499                                if (isDebugging)
500                                        log.debug("Total search time was "
501                                                        + (System.currentTimeMillis() - totalSearchTime)
502                                                        + "ms");
503
504                        } catch (RepositoryException re) {
505                                re.printStackTrace();
506                                MessageHandler
507                                                .error("There was a problem searching the Repository. Please check your network connection.\n",
508                                                                re);
509                        } catch (AuthenticationException ae) {
510                                if (ae.getType() != AuthenticationException.USER_CANCEL) {
511                                        MessageHandler
512                                                        .error("Authentication error. Please ensure your username and password were spelled correctly,\nand verify your network connection\n",
513                                                                        ae);
514                                        ae.printStackTrace();
515                                }
516                        } catch (Exception exception) {
517                                exception.printStackTrace();
518                                MessageHandler.error("Error.\n", exception);
519                        } finally {
520                                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
521                                /*
522                                 * Wait cursor is not set when hitting enter in text field.
523                                 * http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5581
524                                 * 
525                                 * However this doesn't help:
526                                 * 
527                                if (component != null){
528                                        component.requestFocus();
529                                        component.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
530                                }
531                                */
532                        }
533                }
534
535        }
536
537        /**
538         * Listener used to changes from the NewFolderFrame
539         */
540        public class LibraryActionHandler implements ActionListener {
541                /**
542                 * Description of the Method
543                 * 
544                 * @param event
545                 *            Description of Parameter
546                 */
547                public void actionPerformed(ActionEvent event) {
548                        if (isDebugging) {
549                                log.debug("LibraryActionHandler.actionPerformed(ActionEvent)");
550                        }
551                        String command = event.getActionCommand();
552                        if (command.equals("new_folder_created")
553                                        || command.equals("new_actor_created")) {
554                                // _library.clearSelection(); //clear the current selections
555                                // LibrarySearchResultPane.collapseAll(_library);
556                                /*
557                                 * TreeAction action = (TreeAction)event.getSource(); TreePath
558                                 * path = action.getPath(); _library.addSelectionPath(path);
559                                 * System.out.println("path expanded.");
560                                 * //LibrarySearchResultPane.expandAll(_library);
561                                 * _library.expandPath(path);
562                                 */
563                        }
564                }
565        }
566
567        /**
568         * Inner class to handle changes to the Ontology Selection box.
569         * 
570         * @author Aaron Schultz
571         */
572        public class OntSelectActionHandler implements ActionListener {
573                private boolean _actionDisabled = false;
574
575                public void disableAction(boolean actionDisabled) {
576                        _actionDisabled = actionDisabled;
577                }
578
579                public void actionPerformed(ActionEvent e) {
580                        if (!_actionDisabled) {
581
582                                if (e.getSource() == _ontSelectBox) {
583
584                                        String selectedOntology = (String) _ontSelectBox
585                                                        .getSelectedItem();
586
587                                        try {
588
589                                                if (selectedOntology.equals(allOntologiesName)) {
590                                                        _libraryPanel.update(new LibrarySearchResults());
591                                                } else {
592                                                        EntityLibrary selOntLibrary = null;
593                                                        int cnt = _libraryModel.getChildCount(_libraryModel
594                                                                        .getRoot());
595                                                        for (int i = 0; i < cnt; i++) {
596                                                                NamedObj child = (NamedObj) _libraryModel
597                                                                                .getChild(_libraryModel.getRoot(), i);
598                                                                if (child.getName().equals(selectedOntology)) {
599                                                                        if (child instanceof EntityLibrary) {
600                                                                                selOntLibrary = (EntityLibrary) child;
601                                                                        }
602                                                                        break;
603                                                                }
604                                                        }
605                                                        if (selOntLibrary == null) {
606                                                                String message = "No Components found in "
607                                                                                + selectedOntology;
608                                                                JOptionPane.showMessageDialog(getParentFrame(),
609                                                                                message);
610                                                                _libraryPanel
611                                                                                .update(new LibrarySearchResults());
612                                                                _ontSelectBox
613                                                                                .setSelectedItem(allOntologiesName);
614                                                        } else {
615                                                                ((ResultTreeRebuilder) _libraryPanel)
616                                                                                .update(selOntLibrary);
617                                                                _librarySearchPane.clearSearch();
618                                                        }
619                                                }
620                                        } catch (IllegalActionException iae) {
621                                                // FIXME: handle this exception correctly
622                                                System.out.println("Error searching: "
623                                                                + iae.getMessage());
624                                                // iae.printStackTrace();
625                                        }
626                                }
627                        }
628                }
629        }
630
631        /**
632         * A factory that creates the library panel for the editors.
633         * 
634         * @author Aaron Schultz
635         */
636        public static class Factory extends TabPaneFactory {
637                /**
638                 * Create a factory with the given name and container.
639                 * 
640                 * @param container
641                 *            The container.
642                 * @param name
643                 *            The name of the entity.
644                 * @exception IllegalActionException
645                 *                If the container is incompatible with this attribute.
646                 * @exception NameDuplicationException
647                 *                If the name coincides with an attribute already in the
648                 *                container.
649                 */
650                public Factory(NamedObj container, String name)
651                                throws IllegalActionException, NameDuplicationException {
652                        super(container, name);
653                }
654
655                /**
656                 * Create a library pane that displays the given library of actors.
657                 * 
658                 * @return A new LibraryPaneTab that displays the library
659                 */
660                public TabPane createTabPane(TableauFrame parent) {
661                        ComponentLibraryTab clt = new ComponentLibraryTab(parent);
662                        clt.setTabName(this.getName());
663                        return clt;
664                }
665        }
666}