001/*
002 * Copyright (c) 2004-2011 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2014-08-22 18:21:57 +0000 (Fri, 22 Aug 2014) $' 
007 * '$Revision: 32916 $'
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.kar;
031
032import java.awt.Color;
033import java.awt.event.ActionEvent;
034import java.io.File;
035import java.io.IOException;
036
037import javax.swing.JFileChooser;
038import javax.swing.JOptionPane;
039
040import org.apache.commons.logging.Log;
041import org.apache.commons.logging.LogFactory;
042import org.kepler.gui.KeplerGraphFrame;
043import org.kepler.gui.ModelToFrameManager;
044import org.kepler.kar.KARFile;
045import org.kepler.kar.KARManager;
046import org.kepler.kar.SaveKAR;
047import org.kepler.objectmanager.cache.LocalRepositoryManager;
048import org.kepler.objectmanager.library.LibraryManager;
049import org.kepler.objectmanager.lsid.KeplerLSID;
050import org.kepler.objectmanager.repository.Repository;
051import org.kepler.objectmanager.repository.RepositoryManager;
052import org.kepler.sms.SMSServices;
053import org.kepler.sms.gui.SemanticTypeEditor;
054import org.kepler.util.DotKeplerManager;
055import org.kepler.util.FileUtil;
056import org.kepler.util.RenameUtil;
057
058import ptolemy.actor.gui.Effigy;
059import ptolemy.actor.gui.PtolemyEffigy;
060import ptolemy.actor.gui.PtolemyFrame;
061import ptolemy.actor.gui.Tableau;
062import ptolemy.actor.gui.TableauFrame;
063import ptolemy.gui.ExtensionFilenameFilter;
064import ptolemy.gui.JFileChooserBugFix;
065import ptolemy.gui.PtFileChooser;
066import ptolemy.gui.PtGUIUtilities;
067import ptolemy.kernel.ComponentEntity;
068import ptolemy.kernel.Entity;
069import ptolemy.kernel.util.IllegalActionException;
070import ptolemy.kernel.util.NamedObj;
071import ptolemy.util.MessageHandler;
072import ptolemy.vergil.basic.BasicGraphFrame;
073import ptolemy.vergil.toolbox.FigureAction;
074
075/**
076 * This action exports a workflow as a kar file. Subclasses of this class should
077 * be used for saving any type of KAR file. To make a subclass you will want to
078 * override the initialize method and the handle action method. See
079 * ExportActorArchiveAction for an example subclass.
080 * 
081 * @author Aaron Schultz, Christopher Brooks
082 */
083public class ExportArchiveAction extends FigureAction {
084
085        private static final Log log = LogFactory.getLog(ExportArchiveAction.class);
086        private static final boolean isDebugging = log.isDebugEnabled();
087        
088        // "A" should probably be used for a too-be-implemeted Edit Select All.
089        //private static KeyStroke ACCELERATOR_KEYSTROKE = KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, 
090        //              Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
091
092        protected TableauFrame _parent;
093
094        protected boolean _saveSucceeded = false;
095
096        /**
097         * The SaveKAR object is a GUI free helper class for saving KARs.
098         */
099        protected SaveKAR _savekar = null;
100
101        /**
102         * A string that represents to the user the type of KAR that is being
103         * exported. This string may appear in dialog messages to allow different
104         * callers to better identify to the user what the contents of this KAR file
105         * are. This should be the first thing set in the handleType method of any
106         * subclasses of this class but is not required.
107         */
108        protected String _karType = "";
109
110        protected boolean refreshFrameAfterSave = true;
111
112        protected boolean _mapKARToCurrentFrame = true;
113
114        /**
115         * The purpose of KARs are for grouping things together. Sometimes though we
116         * have only one thing in a KAR which changes some things. This is a bit
117         * confusing here because this refers to a single item being saved from the
118         * user perspective. Just because we're saving a single item at this stage
119         * does not mean there will only be one file in the KAR. There may be other
120         * files associated with the single item that we're saving from this class.
121         * For example we may be saving a single workflow that has many files
122         * associated with it.
123         */
124        protected boolean _singleItemKAR = false;
125
126        protected String _defaultFileName = null;
127
128        protected String _overrideModuleDependencies = null;
129        
130        protected boolean _upload = true;
131        
132        protected File _saveFile = null;
133        
134        protected boolean _nonInteractiveSave = false;
135
136    /** The workflow associated with the parent frame. */
137        private ComponentEntity<?> _workflow = null;
138        
139        /**
140         * Call this before actionPerformed to do a 
141         * non-interactive Save, not a Save As...
142         * and don't attempt upload.
143         * @param saveFile
144         */
145        public void setSaveFile(File saveFile){
146                _saveFile = saveFile;
147                _nonInteractiveSave = true;
148                setUpload(false);
149        }
150
151        public boolean isSingleItemKAR() {
152                return _singleItemKAR;
153        }
154
155        public void setSingleItemKAR(boolean singleItemKAR) {
156                _singleItemKAR = singleItemKAR;
157        }
158        
159        /**
160         * @return boolean controlling if upload should
161         * occur
162         */
163        public boolean doUpload(){
164                return _upload;
165        }
166        
167        /**
168         * Set false if you don't want user prompted to upload
169         * after save if they have a remote repository selected.
170         * @param upload
171         */
172        public void setUpload(boolean upload){
173                _upload = upload;
174        }
175
176        public SaveKAR getSaveKAR() {
177                return _savekar;
178        }
179
180        /**
181         * Set default KAR save filename.
182         * 
183         * @param name
184         */
185        public void setDefaultFileName(String name) {
186                _defaultFileName = name;
187        }
188
189        /**
190         * Get default KAR save filename.
191         * 
192         * @return _defaultFileName
193         */
194        public String getDefaultFileName() {
195                return _defaultFileName;
196        }
197
198        /**
199         * @return the refreshFrameAfterSave
200         */
201        public boolean isRefreshFrameAfterSave() {
202                return refreshFrameAfterSave;
203        }
204
205        /**
206         * Allow for toggling the close/open of the main frame after saving. This
207         * makes sense for workflows but not for other types of saves like actors.
208         * 
209         * @param refreshFrameAfterSave
210         *            the refreshFrameAfterSave to set
211         */
212        public void setRefreshFrameAfterSave(boolean refreshFrameAfterSave) {
213                this.refreshFrameAfterSave = refreshFrameAfterSave;
214        }
215
216        /**
217         * Set true when this KAR should not be mapped to this JFrame. 
218         */
219        public void setMapKARToCurrentFrame(boolean mapKARToCurrentFrame){
220                _mapKARToCurrentFrame = mapKARToCurrentFrame;
221        }
222        public boolean mapKARToCurrentFrame(){
223                return _mapKARToCurrentFrame;
224        }
225
226        /**
227         * Convenience reference to the LocalRepositoryManager.
228         */
229        protected LocalRepositoryManager _localRepositoryManager;
230
231        /**
232         * prevent the file chooser from displaying and just use a temp file
233         */
234        public boolean useTempFile = false;
235
236        /**
237         * Constructor
238         * 
239         * @param parent
240         *            the "frame" (derived from ptolemy.gui.Top) where the menu is
241         *            being added.
242         */
243        public ExportArchiveAction(TableauFrame parent) {
244                super("");
245
246                //putValue(ACCELERATOR_KEY, ACCELERATOR_KEYSTROKE);
247                
248                _parent = parent;
249
250                if (parent == null) {
251                        IllegalArgumentException iae = new IllegalArgumentException(
252                                        "ExportArchiveAction constructor received NULL argument for TableauFrame");
253                        iae.fillInStackTrace();
254                        throw iae;
255                }
256
257                _localRepositoryManager = LocalRepositoryManager.getInstance();
258
259                _savekar = new SaveKAR();
260
261                initialize();
262        }
263
264        /**
265         * The initialize method is called at the end of the public constructor.
266         * This makes it easier to change the behavior of constructing a subclass.
267         */
268        protected void initialize() {
269
270                _karType = "workflow";
271
272                this.setSingleItemKAR(true);
273
274                this.putValue("tooltip", "Save a KAR file archive.");
275
276        }
277
278        /**
279         * Invoked when an action occurs.
280         * 
281         * @param e
282         *            ActionEvent
283         */
284        @Override
285    public void actionPerformed(ActionEvent e) {
286                super.actionPerformed(e);
287
288                _saveSucceeded = false;
289
290                // grab the new KAR lsid after the file is saved
291                // in case we need it for later
292                KeplerLSID theNewKARLSID = null;
293
294        // if the frame is for a sub-workflow, ask the user if they
295        // want to save for the sub-workflow only.
296        if(_parent instanceof PtolemyFrame) {
297            NamedObj workflow = ((PtolemyFrame) _parent).getModel();
298            NamedObj topWorkflow = workflow.toplevel();
299            if (workflow != topWorkflow) {
300                if(!MessageHandler.yesNoQuestion("Save sub-workflow only?")) {
301                    _parent = ModelToFrameManager.getInstance().getFrame(topWorkflow);
302                }
303            }
304        }
305
306                
307                // save the window size, position, and zoom
308        if (_parent instanceof BasicGraphFrame) {
309            try {
310                ((BasicGraphFrame) _parent).updateWindowAttributes();
311            } catch (Exception exception) {
312                MessageHandler
313                        .error("Failed to save window size, position and zoom while writing KAR.",
314                                exception);
315            }
316        }               
317                
318                // ////////////////////////////////////////////////
319                // Only this part should be different depending on
320                // where the KAR is being exported from
321                boolean continueExport = handleAction(e);
322                if (!continueExport) {
323                        return;
324                }
325                // ////////////////////////////////////////////////
326
327                // Force single item KAR if there is only one
328                // item in the KAR
329                // This may or may not be good/necessary
330                if (_savekar.getSaveInitiatorList().size() == 1) {
331                        setSingleItemKAR(true);
332                } else {
333                        setSingleItemKAR(false);
334                }
335
336                File saveFile = null;
337
338                if (_nonInteractiveSave) {
339                        saveFile = _saveFile;
340                } else {
341                        if (useTempFile) {
342
343                                // Use a temporary file for saving to in order to simulate
344                                // the old upload to repository function
345                                // don't really want to be doing this...
346                                ComponentEntity<?> ce = _savekar.getSaveInitiatorList().get(0);
347                                String tempFileName = ce.getName() + ".kar";
348                                File tempDir = DotKeplerManager.getInstance()
349                                                .getTransientModuleDirectory("core");
350                                saveFile = new File(tempDir, tempFileName);
351
352                        } else {
353                // Create a file filter that accepts .kar files.
354                ExtensionFilenameFilter filter = new ExtensionFilenameFilter("kar");
355                        // Avoid white boxes in file chooser, see
356                        // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=3801
357                        JFileChooserBugFix jFileChooserBugFix = new JFileChooserBugFix();
358                        Color background = null;
359                        PtFileChooser chooser = null;
360
361                        try {
362                                background = jFileChooserBugFix.saveBackground();
363                                chooser = new PtFileChooser(_parent, "Save",
364                                                JFileChooser.SAVE_DIALOG);
365                                if(_parent instanceof BasicGraphFrame) {
366                                        chooser.setCurrentDirectory(((BasicGraphFrame)_parent).getLastDirectory());
367                                }
368                                chooser.addChoosableFileFilter(filter);
369                                
370                                if(_defaultFileName != null) {
371                                        chooser.setSelectedFile(new File(_defaultFileName));
372                                }
373
374                                int returnVal = chooser.showDialog(_parent, "Save");
375                                if (returnVal == JFileChooser.APPROVE_OPTION) {
376                                        // process the given file
377                                        saveFile = chooser.getSelectedFile();
378                                        
379                                        if(saveFile.exists() && !PtGUIUtilities.useFileDialog()) {
380                            if (!MessageHandler.yesNoQuestion("Overwrite \""
381                                    + saveFile.getName() + "\"?")) {
382                                saveFile = null;
383                            }
384                                        }
385                                }                               
386                        } finally {
387                                jFileChooserBugFix.restoreBackground(background);
388                        }
389
390                        }
391                }
392
393                if (saveFile == null)
394                        return;
395
396                _savekar.setFile(saveFile);
397
398                // see if the name has changed.
399                
400                // make sure there's a reference to the workflow; it could be null when,
401                // e.g., exporting a run in the WRM.
402                if(_workflow != null) {
403                String newName = FileUtil.getFileNameWithoutExtension(saveFile.getName());
404                if(!newName.equals(_workflow.getName())) {
405                        try {
406                                // rename the frame title and workflow xml file in the kar
407                                RenameUtil.renameComponentEntity(_workflow, newName);
408                        } catch(Exception exception) {
409                                MessageHandler.error("Error renaming workflow.", exception);
410                        }
411                }
412                }
413                
414                // See if this file already exists
415                if (_savekar.getFile().exists()) {
416                        try {
417                                // Get the LSID of the existing kar
418                                KARFile existingKARFile = new KARFile(_savekar.getFile());
419                                KeplerLSID existingFileLSID = existingKARFile.getLSID();
420                                // Delete the old kar from the library
421                                LibraryManager lm = LibraryManager.getInstance();
422                                lm.deleteKAR(_savekar.getFile());
423                                // increment the revision and set it
424                                existingFileLSID.incrementRevision();
425                                _savekar.specifyLSID(existingFileLSID);
426                                // save the new kar file
427                                theNewKARLSID = _savekar.saveToDisk(_parent, _overrideModuleDependencies);
428                        } catch (Exception e1) {
429                                e1.printStackTrace();
430                        }
431                } else {
432                        theNewKARLSID = _savekar.saveToDisk(_parent, _overrideModuleDependencies);
433                }
434
435                if (theNewKARLSID != null) {
436                        _saveSucceeded = true;
437                        
438                        // Add JFrame=>KARFile mapping to KARManager, unless ifRefreshFrameAfterSave, 
439                        // in which case the mapping is added during ActorMetadataKAREntry.open, or if
440                        // mapKARToCurrentFrame was explicitly set false.
441                        if (!isRefreshFrameAfterSave() && mapKARToCurrentFrame()){
442                                try {
443                                        KARFile karFile = new KARFile(_savekar.getFile());
444                                        KARManager.getInstance().add(_parent, karFile);
445                                } catch (IOException e1) {
446                                        // TODO Auto-generated catch block
447                                        e1.printStackTrace();
448                                }
449                        }
450
451                        if(_parent instanceof KeplerGraphFrame) {
452                                try {
453                                        ((KeplerGraphFrame) _parent).updateHistory(_savekar.getFile().getAbsolutePath());
454                                } catch (IOException e1) {
455                                        MessageHandler.error("Unable to update history menu.", e1);
456                                }
457                        }
458                        if(_parent instanceof BasicGraphFrame) {
459                                ((BasicGraphFrame)_parent).setLastDirectory(saveFile.getParentFile());
460                        }
461                }
462
463                // After the KAR has been saved to disk we add it to the cache
464                // then add it to the library and refresh the JTrees
465                if (!useTempFile) {
466
467                        // If it is saved in a local repository update the library
468                        LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
469                        if (lrm.isInLocalRepository(_savekar.getFile())) {
470
471                                _savekar.saveToCache();
472
473                                LibraryManager lm = LibraryManager.getInstance();
474                                try {
475                                        lm.addKAR(_savekar.getFile());
476                                } catch (Exception e2) {
477                                        JOptionPane.showMessageDialog(_parent,
478                                                        "Error adding kar to library: " + e2.getMessage());
479                                }
480                                try {
481                                        lm.refreshJTrees();
482
483                                } catch (IllegalActionException e2) {
484                                        e2.printStackTrace();
485                                }
486                        } else {
487                                // JOptionPane.showMessageDialog(_parent,
488                                // "KAR file successfully saved to a folder that is not designated as a Local Repository.  In order for the contents of this KAR to show up in the component library, you can move the KAR file to a Local Repository and restart Kepler, or you can add the folder as a local repository by using the Component 'Sources' button.");
489                        }
490                }
491
492                // Now we
493                // 1. check to see if there is a remote repository selected by the user
494                // for saving
495                // 2. double check with the user that they want to send the KAR
496                // to the remote repository,
497                // 3. then upload it
498                try {
499                        if (_upload) {
500                                RepositoryManager rm = RepositoryManager.getInstance();
501                                Repository remoteRepo = rm.getSaveRepository();
502                                if (remoteRepo != null) {
503                                        boolean continueWithUpload = true;
504                                        int choice = JOptionPane.showConfirmDialog(_parent,
505                                                        "Would you like to upload this KAR to the remote repository?\n"
506                                                                        + "     " + remoteRepo.getName() + " at "
507                                                                        + remoteRepo.getRepository() + "\n",
508                                                        "Upload To Repository", JOptionPane.YES_NO_OPTION);
509                                        if (choice != JOptionPane.YES_OPTION) {
510                                                continueWithUpload = false;
511                                        }
512
513                                        if (continueWithUpload) {
514
515                                                try {
516                                                        ComponentUploader uploader = new ComponentUploader(
517                                                                        _parent);
518                                                        KARFile kf2upload = new KARFile(getSaveKAR()
519                                                                        .getFile());
520                                                        uploader.upload(kf2upload);
521                                                } catch (Exception ee) {
522                                                        ee.printStackTrace();
523                                                }
524
525                                        }
526
527                                }
528                        }
529
530                        if (isRefreshFrameAfterSave()) {
531                                // Open a new frame and close the old one
532                                KARFile karf;
533                                try {
534                                        
535                                        //move old frame close before new frame open. 
536                                        //It will fix the bug http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5200 without having memory leak.
537                                        karf = new KARFile(_savekar.getFile());
538                                        karf.openKARContents(_parent, false);
539                                        karf.close();
540                                        
541                                        // dispose the old window after opening a new one
542                                        _parent.dispose();
543                                        
544                                } catch (Exception e1) {
545                                        e1.printStackTrace();
546                                }
547                        }
548                } catch (IOException e1) {
549                        e1.printStackTrace();
550                } catch (Exception e1) {
551                        e1.printStackTrace();
552                }
553
554        }
555
556        /**
557         * @return whether or not the file was saved to disk
558         */
559        public boolean saveSucceeded() {
560                return _saveSucceeded;
561        }
562
563        /**
564         * This method will set up the SaveKAR object in the case of saving a
565         * workflow. To save other types of KARs a subclass that overrides this
566         * method can be used to add multiple workflows to the SaveKAR object and to
567         * identify other objects that should be added to the KAR through the
568         * appropriate KAREntryHandlers.
569         * 
570         * @param e
571         * @return boolean true if the export should continue
572         */
573        protected boolean handleAction(ActionEvent e) {
574
575                // get the workflow entity from parent
576                Tableau tableau = _parent.getTableau();
577                Effigy effigy = (Effigy) tableau.getContainer();
578                Entity<?> entity = null;
579                if (effigy instanceof PtolemyEffigy) {
580                        entity = (Entity<?>) ((PtolemyEffigy) effigy).getModel();
581                }
582
583                if (entity == null){
584                        return false;
585                }
586
587                if (!checkSingleObject(entity, false)) {
588                        return false;
589                }
590
591                _workflow = (ComponentEntity<?>) entity;
592                
593                // When it's ready to go add it and continue
594                _savekar.addSaveInitiator((ComponentEntity<?>) entity);
595                return true;
596        }
597
598        protected void overrideModuleDependencies(String moduleDependencies) {
599                _overrideModuleDependencies = moduleDependencies;
600        }
601
602        /**
603         * Check a single NamedObj for LSID, name, and SemanticType.
604         * 
605         * @param checkIfSemenaticallyAnnotated If is true, and entity has no
606         * semantic annotations, user is warned, but not required, to add
607         * annotations.
608         * 
609         * @return Returns true if the export should continue
610         */
611        protected boolean checkSingleObject(NamedObj entity,
612                        boolean checkIfSemenaticallyAnnotated) {
613
614                // Make sure it has an LSID
615                _savekar.checkNamedObjLSID(entity);
616
617                if (entity instanceof ComponentEntity) {
618                        // Make sure it has a Name
619                        // Query the user for a name if needed
620                        try {
621                                _savekar.checkNamedObjName(entity);
622
623                                // FIXME: shouldn't really need this
624                                // LSID should be the unique identifier for the library
625                                /*
626                                 * LibraryManager lm = LibraryManager.getInstance(); if (lm ==
627                                 * null || lm.componentNameInUse(entity.getName())) { throw new
628                                 * NameDuplicationException(null, ""); }
629                                 */
630
631                        } catch (Exception e1) {
632
633                                // / TODO
634                                // / it might be good to just go through the usual saveAs route
635                                // here, but
636                                // / it's challenging since the frame closes and a new is opened
637                                // without
638                                // / returning a ref. Also the KAR system reopens everything
639                                // once done
640                                // / (which would result in two of the same workflow being
641                                // open).
642                                // /kgf = (KeplerGraphFrame) this._parent;
643                                // /kgf._saveAs(".xml");
644
645                                // TODO very similar to code in RenameComponentEntityAction,
646                                // find a way to merge?
647                                String message = "Please enter a name";
648                                if (!_karType.equals("")) {
649                                        message += " for this " + _karType;
650                                }
651                                message += ": ";
652
653                                String warnMessage = "ERROR name cannot contain the < sign";
654
655                                String newName = JOptionPane.showInputDialog(message,
656                                                entity.getName());
657                                if (newName == null) {
658                                        // user hit the cancel button
659                                        return false;
660                                }
661
662                                int lessThan = newName.indexOf("<");
663                                if (lessThan >= 0) {
664                                        JOptionPane.showMessageDialog(_parent, warnMessage,
665                                                        "Error", JOptionPane.ERROR_MESSAGE);
666                                        return false;
667                                }
668
669                                try {
670
671                                        RenameUtil.renameComponentEntity((ComponentEntity<?>) entity,
672                                                        newName);
673                                        _parent.setTitle(entity.getName());
674                                        setDefaultFileName(newName + ".kar");
675
676                                } catch (Exception e) {
677                                        e.printStackTrace();
678                                        JOptionPane
679                                                        .showMessageDialog(_parent, "A problem occured.");
680                                        return false;
681                                }
682
683                                try {
684                                        _savekar.checkNamedObjName(entity);
685                                } catch (Exception e2) {
686                                        if (e2.getMessage().equals("Unnamed")) {
687                                                // continue using the unnamed name
688                                        } else {
689                                                JOptionPane.showMessageDialog(_parent,
690                                                                "KAR contents must have a name.");
691                                                // stop the export
692                                                return false;
693                                        }
694                                }
695                        }
696                }
697
698                // Check to see if it has at least one SemanticType
699                // Ask the user if they want to add one if it doesn't
700                if (checkIfSemenaticallyAnnotated
701                                && SMSServices.getActorSemanticTypes(entity).size() == 0) {
702
703                        String message = "In order for KAR item: " + entity.getName()
704                                        + " to show up in an Ontology"
705                                        + " it must contain at least one Semantic Type.\n"
706                                        + " Would you like to add a Semantic Type? ";
707                        String title = "Add Semantic Types?";
708                        int choice = JOptionPane.showConfirmDialog(_parent, message, title,
709                                        JOptionPane.YES_NO_OPTION);
710                        if (choice == JOptionPane.YES_OPTION) {
711
712                                SemanticTypeEditor editor = new SemanticTypeEditor(_parent,
713                                                entity);
714                                editor.setModal(true);
715                                editor.setVisible(true);
716                        }
717
718                }
719
720                return true;
721
722        }
723}