001/*
002 * Copyright (c) 2012 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-07-14 17:09:46 +0000 (Tue, 14 Jul 2015) $' 
007 * '$Revision: 33539 $'
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 */
029package org.kepler.gui.kar;
030
031import java.io.File;
032import java.io.IOException;
033import java.io.InputStream;
034import java.net.URI;
035import java.net.URL;
036
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039import org.kepler.kar.KAREntry;
040import org.kepler.kar.KARFile;
041import org.kepler.kar.handlers.ActorMetadataKAREntryHandler;
042import org.kepler.objectmanager.lsid.KeplerLSID;
043
044import ptolemy.actor.gui.Effigy;
045import ptolemy.actor.gui.PtolemyEffigy;
046import ptolemy.actor.gui.TableauFrame;
047import ptolemy.kernel.CompositeEntity;
048import ptolemy.kernel.util.IllegalActionException;
049import ptolemy.kernel.util.NameDuplicationException;
050import ptolemy.kernel.util.NamedObj;
051import ptolemy.kernel.util.Workspace;
052import ptolemy.moml.MoMLParser;
053import ptolemy.util.MessageHandler;
054import ptolemy.util.StringUtilities;
055
056/** An Effigy for KAR files.
057 * 
058 *  @author Daniel Crawl
059 *  @version $Id: KAREffigy.java 33539 2015-07-14 17:09:46Z crawl $
060 */
061public class KAREffigy extends PtolemyEffigy {
062
063    public KAREffigy(Workspace workspace) {
064        super(workspace);
065    }
066
067    public KAREffigy(CompositeEntity container, String name)
068            throws IllegalActionException, NameDuplicationException {
069        super(container, name);
070    }
071    
072    /** Get the KAR file. */
073    public KARFile getKARFile() {
074        return _karFile;
075    }
076    
077    /** Open the non-actor entries in the KAR file, e.g., report layout.
078     *  @param frame the frame in which to open the entries
079     *  @param openActorEntries if true, open actor entries.
080     */
081    public void openKAREntries(TableauFrame frame, boolean openActorEntries) {
082        if(_karFile != null) {
083                        for (KAREntry entry : _karFile.karEntries()) {
084                                if (openActorEntries || !ActorMetadataKAREntryHandler.handlesClass(entry.getType())) {
085                                        _karFile.open(entry, frame);
086                                }
087                        }
088        }
089    }
090    
091    /** Set the KAR file. */
092    public void setKARFile(KARFile karFile) {
093        _karFile = karFile;
094    }
095    
096    /** Write the model associated with this effigy. Currently only saving
097     *  the MoML is supported. 
098     */
099    @Override
100    public void writeFile(File file) throws IOException {
101        String path = file.getAbsolutePath();
102        // see if we're saving the MoML
103        // this can happen with File->Export->XML
104        if(path.toLowerCase().endsWith(".xml")) {
105            super.writeFile(file);
106        } else if(path.toLowerCase().endsWith(".kar")) {
107            throw new IOException("writeFile() does not support writing KAR files.");
108        } else {
109            throw new IOException("unknown type of file: " + path);
110        }
111    }
112
113    ///////////////////////////////////////////////////////////////////
114    ////                         inner classes                     ////
115
116    /** A factory for creating new KAR effigies. */
117    public static class Factory extends PtolemyEffigy.FactoryWithoutNew {
118        /** Create a factory with the given name and container.
119         *  @param container The container.
120         *  @param name The name.
121         *  @exception IllegalActionException If the container is incompatible
122         *   with this entity.
123         *  @exception NameDuplicationException If the name coincides with
124         *   an entity already in the container.
125         */
126        public Factory(CompositeEntity container, String name)
127                throws IllegalActionException, NameDuplicationException {
128            super(container, name);
129        }
130        
131        /** Create a new effigy in the given container by reading the
132         *  <i>input</i> URL. If the URL is a KAR file, attempt to open
133         *  it and extract the model. If there are missing module dependencies,
134         *  the user is asked if she wants to ignore the dependencies and
135         *  try to open the KAR, download the dependencies and restart Kepler,
136         *  or do nothing. In the last case this method returns null.
137         *  @param container The container for the effigy.
138         *  @param base The base for relative file references, or null if
139         *   there are no relative file references.
140         *  @param input The input URL.
141         *  @return A new instance of PtolemyEffigy, or null if the URL
142         *   does not specify a Ptolemy II model.
143         *  @exception Exception If the URL cannot be read, or if the data
144         *   is malformed in some way.
145         */
146        @Override
147        public Effigy createEffigy(CompositeEntity container, URL base,
148                URL input) throws Exception {
149            if (input == null) {
150                return super.createEffigy(container, base, input);
151            } else {
152                String extension = getExtension(input).toLowerCase();
153                if(extension.equals("kar")) {
154
155                        KAREffigy effigy = new KAREffigy(container, container.uniqueName("effigy"));
156                        
157                        File file = new File(input.toURI());
158                                        KARFile karf = new KARFile(file);
159                                        
160                                        // see if it's openable
161                                        if(!karf.isOpenable()) {
162                                                // see if dependencies are missing
163                                                if(!karf.areAllModuleDependenciesSatisfied()) {
164                                                        ImportModuleDependenciesAction action = new ImportModuleDependenciesAction(new TableauFrame());
165                                                        action.setArchiveFile(file);
166                                                        ImportModuleDependenciesAction.ImportChoice choice = action.checkDependencies();
167                                                        if(choice == ImportModuleDependenciesAction.ImportChoice.DO_NOTHING) {
168                                    karf.close();
169                                                                return null;
170                                                        } else if(choice == ImportModuleDependenciesAction.ImportChoice.DOWNLOADING_AND_RESTARTING) {
171                                                            action.waitForDownloadAndRestart();
172                                                        }
173                                                } else {
174                                                        MessageHandler.error("This KAR cannot be opened.");
175                                                        karf.close();
176                                                        return null;
177                                                }
178                                        }
179                                        
180                                        effigy.setKARFile(karf);
181                                        karf.cacheKARContents();
182                        
183                                        // For each Actor in the KAR open the MOML
184                                        for (KAREntry entry : karf.karEntries()) {
185                                                KeplerLSID lsid = entry.getLSID();
186                                                //System.out.println("Processing entry, LSID=" + lsid + ", type=" + entry.getType());
187                                                if(isDebugging) {
188                                                    log.debug("Processing entry, LSID=" + lsid + ", type=" + entry.getType());
189                                                }
190                                                
191                                                if (ActorMetadataKAREntryHandler.handlesClass(entry.getType())) {
192                                                                                                        
193                                                        NamedObj toplevel;
194                                                        
195                                                    MoMLParser parser = new MoMLParser();
196                                                    
197                                                    parser.reset();
198                                                    
199                                                    InputStream stream = null;
200                                                    try {
201                                                        stream = karf.getInputStream(entry);
202                                                        toplevel = parser.parse(base, file.getCanonicalPath(), stream);
203                                                    } finally {
204                                                        if(stream != null) {
205                                                            stream.close();
206                                                        }
207                                                    }
208                                                    
209                                                        // make sure we were able to parse or load the NamedObj
210                                                        if(toplevel == null) {
211                                                            return null;
212                                                        }
213                                                        
214                                                        effigy.setModel(toplevel);
215                                                        
216                                URI inputURI = null;
217        
218                                try {
219                                    inputURI = new URI(input.toExternalForm());
220                                } catch (java.net.URISyntaxException ex) {
221                                    // This is annoying, if the input has a space
222                                    // in it, then we cannot create a URI,
223                                    // but we could create a URL.
224                                    // If, under Windows, we call
225                                    // File.createTempFile(), then we are likely
226                                    // to get a pathname that has space.
227                                    // FIXME: Note that jar urls will barf if there
228                                    // is a %20 instead of a space.  This could
229                                    // cause problems in Web Start
230                                    String inputExternalFormFixed = StringUtilities
231                                            .substitute(input.toExternalForm(),
232                                                    " ", "%20");
233        
234                                    try {
235                                        inputURI = new URI(inputExternalFormFixed);
236                                    } catch (Exception ex2) {
237                                        throw new Exception("Failed to generate "
238                                                + "a URI from '"
239                                                + input.toExternalForm()
240                                                + "' and from '"
241                                                + inputExternalFormFixed + "'", ex);
242                                    }
243                                }
244        
245                                // This is used by TableauFrame in its
246                                //_save() method.
247                                effigy.uri.setURI(inputURI);
248                                                }
249                                        }
250                        
251                        return effigy;
252                }
253            }
254            return null;
255        }
256    }
257        
258        private static final Log log = LogFactory.getLog(KAREffigy.class.getName());
259        private static final boolean isDebugging = log.isDebugEnabled();
260
261        /** The KAR file. */
262        private KARFile _karFile;       
263}