001/*
002 * Copyright (c) 2005-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
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.sms.gui;
031
032import java.awt.Dimension;
033import java.awt.Frame;
034import java.awt.event.ActionEvent;
035import java.awt.event.ActionListener;
036import java.util.Iterator;
037import java.util.Vector;
038
039import javax.swing.BorderFactory;
040import javax.swing.Box;
041import javax.swing.BoxLayout;
042import javax.swing.DefaultCellEditor;
043import javax.swing.JButton;
044import javax.swing.JComboBox;
045import javax.swing.JLabel;
046import javax.swing.JOptionPane;
047import javax.swing.JPanel;
048import javax.swing.JScrollPane;
049import javax.swing.JTable;
050import javax.swing.ListSelectionModel;
051import javax.swing.event.TableModelEvent;
052import javax.swing.table.AbstractTableModel;
053import javax.swing.table.TableColumn;
054
055import org.kepler.sms.NamedOntClass;
056import org.kepler.sms.NamedOntModel;
057import org.kepler.sms.OntologyCatalog;
058import org.kepler.sms.SemanticType;
059
060import ptolemy.kernel.util.NamedObj;
061
062/**
063 * 
064 * @author Shawn Bowers
065 */
066public class SemanticLinkTable extends JPanel {
067
068        /**
069         * This is the default constructor
070         * 
071         * @param owner
072         *            The frame that owns the dialog.
073         */
074        public SemanticLinkTable(Frame owner) {
075                super();
076                _owner = owner;
077                _catalog = OntologyCatalog.instance();
078                // set up pane
079                setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
080                add(_createTable());
081                add(Box.createRigidArea(new Dimension(5, 0)));
082                add(_createButtons());
083                // set size
084                setSize(450, 285);
085        }
086
087        /**
088         * Add an object to the set of objects being annotated within this panel.
089         * 
090         * @param obj
091         *            The object to add.
092         */
093        public void addLinkDomainObject(NamedObj obj) {
094                if (obj == null)
095                        return;
096
097                for (Iterator iter = _linkDomainObjects.iterator(); iter.hasNext();) {
098                        Object[] entry = (Object[]) iter.next();
099                        if (entry[0].equals(obj))
100                                return;
101                }
102                Object[] entry = new Object[2];
103                entry[0] = obj;
104                entry[1] = _initializeNamedObj(obj);
105                _linkDomainObjects.add(entry);
106        }
107
108        /**
109         * @return The set of linkDomain objects being annotated with the panel.
110         */
111        public Vector getLinkDomainObjects() {
112                Vector result = new Vector();
113                for (Iterator iter = _linkDomainObjects.iterator(); iter.hasNext();) {
114                        Object[] entry = (Object[]) iter.next();
115                        result.add(entry[0]);
116                }
117                return result;
118        }
119
120        /**
121         * Causes the given object to become visible in the given display. The given
122         * linkDomain object is added if it is not currently one of the linkDomain
123         * objects of the panel.
124         * 
125         * @param obj
126         *            The linkDomain object to make visible.
127         */
128        public void setLinkDomainObjectVisible(NamedObj obj) {
129                // add it if it doesn't exist
130                addLinkDomainObject(obj);
131                _SemLinkTableModel model = null;
132                for (Iterator iter = _linkDomainObjects.iterator(); iter.hasNext();) {
133                        Object[] entry = (Object[]) iter.next();
134                        if (entry[0].equals(obj)) {
135                                model = (_SemLinkTableModel) entry[1];
136                                break;
137                        }
138                }
139                if (model == null)
140                        return;
141                _table.setModel(model);
142        }
143
144        /**
145         * FIXME: Fill in...
146         * 
147         * @return The set of linkDomain objects whose semantic types that have been
148         *         modified.
149         */
150        public Vector getModifiedLinkDomainObjects() {
151                return getLinkDomainObjects();
152        }
153
154        /**
155         * FIXME: Fill in...
156         */
157        public boolean commitLinkDomainObjects() {
158                return true;
159        }
160
161        /**
162         * Initialize the table
163         */
164        private JPanel _createTable() {
165                // text label
166                JPanel txtPane = new JPanel();
167                txtPane.setLayout(new BoxLayout(txtPane, BoxLayout.LINE_AXIS));
168                txtPane.add(new JLabel("Semantic Links"));
169                txtPane.add(Box.createHorizontalGlue());
170
171                // set up the table
172                _table = new JTable(new _SemLinkTableModel()); // 0 row and 3 columns
173                _table.setPreferredScrollableViewportSize(new Dimension(325, 80));
174
175                // set up where the column draws ontologies from
176                TableColumn ontoColumn = _table.getColumnModel().getColumn(1);
177                JComboBox comboBox = new JComboBox();
178                comboBox.setEditable(true);
179                for (Iterator iter = _catalog.getOntologyNames(); iter.hasNext();)
180                        comboBox.addItem("" + iter.next());
181                ontoColumn.setCellEditor(new DefaultCellEditor(comboBox));
182
183                // misc. config.
184                _table.setColumnSelectionAllowed(false);
185                _table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
186
187                // set up the overall pane
188                JPanel pane = new JPanel();
189                pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
190                pane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
191                pane.add(txtPane);
192                pane.add(Box.createRigidArea(new Dimension(0, 5)));
193                pane.add(new JScrollPane(_table));
194
195                // return the pane
196                return pane;
197        }
198
199        /**
200         * Initialize the bottom buttons (close)
201         */
202        private JPanel _createButtons() {
203                JPanel pane = new JPanel();
204                pane.setLayout(new BoxLayout(pane, BoxLayout.LINE_AXIS));
205                pane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
206                pane.add(Box.createHorizontalGlue());
207                pane.add(_addBtn);
208                pane.add(Box.createRigidArea(new Dimension(10, 0)));
209                pane.add(_removeBtn);
210                pane.add(Box.createRigidArea(new Dimension(10, 0)));
211
212                // init buttons
213                _addBtn.setActionCommand("add");
214                _addBtn.setToolTipText("Add new row for semantic types");
215                _addBtn.addActionListener(_buttonListener);
216                _addBtn.setEnabled(false);
217                _removeBtn.setActionCommand("remove");
218                _removeBtn.setToolTipText("Remove selected semantic type");
219                _removeBtn.addActionListener(_buttonListener);
220                _removeBtn.setEnabled(false);
221
222                return pane;
223        }
224
225        /**
226         * anonymous class to handle button events
227         */
228        private ActionListener _buttonListener = new ActionListener() {
229                public void actionPerformed(ActionEvent e) {
230                        if (e.getActionCommand().equals("add")) {
231                                // add a new row to the table
232                                _SemLinkTableModel model = (_SemLinkTableModel) _table
233                                                .getModel();
234                                model.insertEmptyRow();
235                        } else if (e.getActionCommand().equals("remove")) {
236                                // remove current row
237                                int rowIndex = _table.getSelectedRow();
238                                if (rowIndex == -1)
239                                        return;
240                                _SemLinkTableModel model = (_SemLinkTableModel) _table
241                                                .getModel();
242                                model.removeRow(rowIndex);
243                        }
244                }
245        };
246
247        /**
248         * inner class for managing the table data
249         */
250        private class _SemLinkTableModel extends AbstractTableModel {
251                // private members
252                private String[] tableHeader = { "Ontology", "Property", "Range",
253                                "Conditions" };
254                private Vector tableData = new Vector(); // vector of arrays
255
256                /**
257                 * get the column count
258                 */
259                public int getColumnCount() {
260                        return tableHeader.length;
261                }
262
263                /**
264                 * get the row cound
265                 */
266                public int getRowCount() {
267                        return tableData.size();
268                }
269
270                /**
271                 * get the column name
272                 */
273                public String getColumnName(int columnIndex) {
274                        return tableHeader[columnIndex];
275                }
276
277                /**
278                 * get the value of a cell
279                 */
280                public Object getValueAt(int rowIndex, int columnIndex) {
281                        Object[] row = (Object[]) tableData.elementAt(rowIndex);
282                        return row[columnIndex];
283                }
284
285                /**
286                 * everything is editable
287                 */
288                public boolean isCellEditable(int rowIndex, int columnIndex) {
289                        return true;
290                }
291
292                /**
293                 * sets the value in the column. checks that the term is valid?
294                 */
295                public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
296                        if (!validRowIndex(rowIndex))
297                                return;
298                        Object[] row = (Object[]) tableData.elementAt(rowIndex);
299                        row[columnIndex] = aValue;
300                        fireTableChanged(new TableModelEvent(this)); // change event
301                }
302
303                /**
304                 * Insert a new named ontology class into the model
305                 */
306                public void insertRow(NamedOntClass cls) {
307                        Object[] row = new Object[2];
308                        row[1] = cls.getOntologyName();
309                        row[2] = cls.getName();
310                        if (rowExists(row)) // if exists, return
311                                return;
312                        // make sure there are no empty rows (if so, to first one)
313                        int index = firstEmptyRow();
314                        if (index != -1)
315                                tableData.setElementAt(row, index);
316                        else
317                                tableData.add(row);
318                        fireTableChanged(new TableModelEvent(this));
319                }
320
321                /**
322                 * @return True if the table contains the given class, and false
323                 *         otherwise.
324                 */
325                public boolean containsRow(NamedOntClass cls) {
326                        Object[] tmpRow = new Object[2];
327                        tmpRow[0] = cls.getOntologyName();
328                        tmpRow[1] = cls.getName();
329                        return rowExists(tmpRow);
330                }
331
332                /**
333                 * @return a list of rows of non-empty Object arrays of arity 2
334                 *         (Object[2])
335                 */
336                public Iterator getRows() {
337                        Vector results = new Vector();
338                        for (Iterator iter = tableData.iterator(); iter.hasNext();) {
339                                Object[] row = (Object[]) iter.next();
340                                if (row[0] != null || row[1] != null || row[2] != null)
341                                        results.add(row);
342                        }
343                        return results.iterator();
344                }
345
346                /**
347                 * insert an empty row
348                 */
349                public void insertEmptyRow() {
350                        tableData.addElement(new Object[3]);
351                        fireTableChanged(new TableModelEvent(this)); // change event
352                }
353
354                /**
355                 * @return the first empty row or -1 if no empties
356                 */
357                private int firstEmptyRow() {
358                        int index = 0;
359                        for (Iterator iter = tableData.iterator(); iter.hasNext(); index++) {
360                                Object[] row = (Object[]) iter.next();
361                                if (row[0] == null && row[1] == null && row[2] == null)
362                                        return index;
363                        }
364                        return -1;
365                }
366
367                /**
368                 * @return true if the row already exists
369                 */
370                private boolean rowExists(Object[] newrow) {
371                        for (Iterator iter = tableData.iterator(); iter.hasNext();) {
372                                Object[] row = (Object[]) iter.next();
373                                if (newrow[0].equals(row[0]) && newrow[1].equals(row[1])
374                                                && newrow[2].equals(row[2]))
375                                        return true;
376                        }
377                        return false;
378                }
379
380                /**
381                 * remove selected row
382                 */
383                public void removeRow(int rowIndex) {
384                        if (!validRowIndex(rowIndex))
385                                return;
386                        tableData.removeElementAt(rowIndex);
387                        fireTableChanged(new TableModelEvent(this)); // change event
388                }
389
390                /**
391                 * check that we are at a valid row
392                 */
393                private boolean validRowIndex(int rowIndex) {
394                        if (rowIndex >= 0 && rowIndex < getRowCount())
395                                return true;
396                        return false;
397                }
398        };
399
400        /**
401         * intializes the table with the named objects current semantic types
402         */
403        private _SemLinkTableModel _initializeNamedObj(NamedObj obj) {
404                // create a new model
405                _SemLinkTableModel model = new _SemLinkTableModel();
406                // add rows to it (named ontology classes of the item)
407                // for(Iterator iter = _getObjectsNamedOntClasses(obj); iter.hasNext();)
408                // {
409                // NamedOntClass cls = (NamedOntClass)iter.next();
410                // model.insertRow(cls);
411                // }
412                return model;
413        }
414
415        /**
416         * @return A list of all named ontology classes for the current named object
417         *         (if one exists)
418         */
419        private Iterator _getObjectsNamedOntClasses(NamedObj obj) {
420                Vector result = new Vector();
421                if (obj == null)
422                        return result.iterator();
423                for (Iterator iter = obj.attributeList(SemanticType.class).iterator(); iter
424                                .hasNext();) {
425                        SemanticType st = (SemanticType) iter.next();
426                        NamedOntClass cls = _catalog.getNamedOntClass(st);
427                        if (cls != null)
428                                result.add(cls);
429                }
430                return result.iterator();
431        }
432
433        /**
434         * Performs the close button operation.
435         */
436        // protected boolean _doClose() {
437        // for(Iterator iter = getLinkDomainObjects().iterator(); iter.hasNext();) {
438        // NamedObj obj = (NamedObj)iter.next();
439        // if(_semTypesChanged(obj)) {
440        // Object[] options = {"Yes", "No", "Cancel"};
441        // String msg = "Would you like to save the changes you made to '" +
442        // obj.getName() + "'?";
443        // int n = JOptionPane.showOptionDialog(this, msg, "Message",
444        // JOptionPane.YES_NO_CANCEL_OPTION,
445        // JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
446        // if(n == 0 && _saveSemLinks()) {
447        // _close();
448        // return true;
449        // }
450        // else if(n ==1) {
451        // _close();
452        // return true;
453        // }
454        // return false;
455        // }
456        // _close();
457        // return true;
458        // }
459        /**
460         * Performs the commit button operation.
461         */
462        // protected void _doCommit() {
463        // if(_semTypesChanged())
464        // _saveSemLinks();
465        // }
466
467        /**
468         * Saves the semantic types in the table to the named object.
469         * 
470         * @return True if the semantic types are well-formed and the save
471         *         succeeded, and false otherwise.
472         */
473        // private boolean _saveSemLinks() {
474        // String msg;
475        // if(!_wellFormedSemanticTypes())
476        // return false;
477        // if(!_accessibleSemanticTypes())
478        // return false;
479        // // remove current semtypes
480        // _removeNamedObjSemtypes();
481        // // add new ones (make sure to remove dups)
482        // _addSemLinks();
483        // // save if in actor library (has id) ... ?
484        // return true;
485        // }
486        /**
487         * Adds the semantic types in the dialog to the entity.
488         */
489        private void _addSemLinks(NamedObj obj, _SemLinkTableModel model) {
490                Vector result = new Vector();
491
492                for (Iterator iter = model.getRows(); iter.hasNext();) {
493                        String namespace = "";
494                        String classname = "";
495                        // get the row
496                        Object[] row = (Object[]) iter.next();
497                        boolean foundModel = false, foundClass = false;
498                        // search for namespace
499                        for (Iterator models = _catalog.getNamedOntModels(); models
500                                        .hasNext();) {
501                                NamedOntModel m = (NamedOntModel) models.next();
502                                if (row[0].equals(m.getName())) {
503                                        namespace = m.getNameSpace();
504                                        foundModel = true;
505                                        // search for classname
506                                        for (Iterator classes = m.getNamedClasses(); classes
507                                                        .hasNext();) {
508                                                NamedOntClass c = (NamedOntClass) classes.next();
509                                                if (row[1].equals(c.getName())) {
510                                                        classname = c.getLocalName();
511                                                        foundClass = true;
512                                                        break;
513                                                }
514                                        }
515                                        break;
516                                }
517                        }
518                        if (!foundModel) {
519                                namespace = row[0].toString();
520                                classname = row[1].toString();
521                        } else if (!foundClass) {
522                                classname = row[1].toString();
523                        }
524                        // create id
525                        if ((namespace + classname).split("#").length != 2) {
526                                String[] ns_array = namespace.split("#");
527                                namespace = "";
528                                for (int i = 0; i < ns_array.length; i++)
529                                        namespace = namespace + ns_array[i];
530                                namespace = namespace + "#";
531                                String[] cl_array = classname.split("#");
532                                classname = "";
533                                for (int i = 0; i < cl_array.length; i++)
534                                        classname = classname + cl_array[i];
535                        }
536                        String id = namespace + classname;
537                        if (!result.contains(id))
538                                result.add(id);
539                }
540                // add the semantic type
541                int i = 0;
542                for (Iterator iter = result.iterator(); iter.hasNext();) {
543                        try {
544                                SemanticType st = new SemanticType(obj, "semanticType" + i++);
545                                st.setExpression((String) iter.next());
546                        } catch (Exception e) {
547                                e.printStackTrace();
548                        }
549                }
550        }
551
552        /**
553         * @return True if every semantic type in the table has an ontology value
554         *         and class value.
555         */
556        private boolean _wellFormedSemanticTypes(_SemLinkTableModel model) {
557                String msg;
558                for (Iterator iter = model.getRows(); iter.hasNext();) {
559                        Object[] row = (Object[]) iter.next();
560                        if (row[0] == null) {
561                                msg = "Commit Error: Every semantic type must have an ontology value.";
562                                JOptionPane.showMessageDialog(this, msg, "Message",
563                                                JOptionPane.ERROR_MESSAGE);
564                                return false;
565                        }
566                        if (row[1] == null) {
567                                msg = "Commit Error: Every semantic type must have a class value.";
568                                JOptionPane.showMessageDialog(this, msg, "Message",
569                                                JOptionPane.ERROR_MESSAGE);
570                                return false;
571                        }
572                }
573                return true;
574        }
575
576        /**
577         * @return True if all the semantic types are "known" to the local version
578         *         of kepler.
579         */
580        private boolean _accessibleSemanticTypes(_SemLinkTableModel model) {
581                String msg;
582                for (Iterator iter = model.getRows(); iter.hasNext();) {
583                        boolean classFound = false;
584                        Object[] row = (Object[]) iter.next();
585                        for (Iterator iter2 = _catalog.getNamedOntModels(); iter2.hasNext();) {
586                                NamedOntModel ontmodel = (NamedOntModel) iter2.next();
587                                if (row[0].equals(ontmodel.getName())
588                                                || row[0].equals(ontmodel.getNameSpace())) {
589                                        for (Iterator iter3 = ontmodel.getNamedClasses(); iter3
590                                                        .hasNext();) {
591                                                NamedOntClass cls = (NamedOntClass) iter3.next();
592                                                if (row[1].equals(cls.getName())
593                                                                || row[1].equals(cls.getLocalName()))
594                                                        classFound = true;
595                                        }
596                                }
597                        }
598                        if (!classFound) {
599                                msg = "Unable to find a matching class for at least one semantic type. \n"
600                                                + "Do you wish to save anyway?";
601                                int n = JOptionPane.showConfirmDialog(this, msg, "Message",
602                                                JOptionPane.YES_NO_OPTION);
603                                if (n == 1)
604                                        return false;
605                                break;
606                        }
607                }
608                return true;
609        }
610
611        /**
612         * Removes the current semtypes on the named object
613         */
614        private void _removeNamedObjSemtypes(NamedObj obj) {
615                if (obj == null)
616                        return;
617
618                Vector semtypes = new Vector();
619                for (Iterator iter = obj.attributeList(SemanticType.class).iterator(); iter
620                                .hasNext();)
621                        semtypes.add((SemanticType) obj);
622
623                for (Iterator iter = semtypes.iterator(); iter.hasNext();) {
624                        SemanticType st = (SemanticType) iter.next();
625                        try {
626                                st.setContainer(null);
627                        } catch (Exception e) {
628                                e.printStackTrace();
629                        }
630                }
631        }
632
633        /**
634         * Really shut the thing down.
635         */
636        private void _close() {
637        }
638
639        /**
640         * @return True if the semantic types have changed since the last commit;
641         *         false otherwise
642         */
643        private boolean _semLinksChanged(NamedObj obj, _SemLinkTableModel model) {
644                // each namedObject semantic type should be in the dialog
645                Vector types = new Vector();
646                for (Iterator iter = _getObjectsNamedOntClasses(obj); iter.hasNext();) {
647                        NamedOntClass cls = (NamedOntClass) iter.next();
648                        if (!model.containsRow(cls))
649                                return true;
650                        types.add(cls);
651                }
652
653                // there shouldn't be more rows
654                Vector rows = new Vector();
655                for (Iterator iter = model.getRows(); iter.hasNext();)
656                        rows.add(iter.next());
657
658                return rows.size() != types.size();
659        }
660
661        // private members
662        private Vector _linkDomainObjects = new Vector();
663        private JTable _table = null; // the ontology, class table
664        private JButton _addBtn = new JButton("Add"); // button for adding rows to
665                                                                                                        // table
666        private JButton _removeBtn = new JButton("Remove"); // button removing items
667        private Frame _owner; // the owner of this dialog
668        private OntologyCatalog _catalog; // holds the ontologies
669}