001/*
002 *  The node controller for actor instances.
003 *  Copyright (c) 2010 The Regents of the University of California.
004 *  All rights reserved.
005 *  Permission is hereby granted, without written agreement and without
006 *  license or royalty fees, to use, copy, modify, and distribute this
007 *  software and its documentation for any purpose, provided that the above
008 *  copyright notice and the following two paragraphs appear in all copies
009 *  of this software.
010 *  IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
011 *  FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
012 *  ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
013 *  THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
014 *  SUCH DAMAGE.
015 *  THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
016 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
017 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
018 *  PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
019 *  CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
020 *  ENHANCEMENTS, OR MODIFICATIONS.
021 *  PT_COPYRIGHT_VERSION_2
022 *  COPYRIGHTENDKEY
023 */
024package org.kepler.kar.handlers;
025
026import java.io.InputStream;
027import java.util.Hashtable;
028import java.util.Vector;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.kepler.kar.KAREntry;
033import org.kepler.kar.KAREntryHandler;
034import org.kepler.kar.KAREntryHandlerFactory;
035import org.kepler.kar.KARFile;
036import org.kepler.kar.KARManager;
037import org.kepler.moml.NamedObjId;
038import org.kepler.objectmanager.ActorMetadata;
039import org.kepler.objectmanager.cache.ActorCacheObject;
040import org.kepler.objectmanager.cache.CacheManager;
041import org.kepler.objectmanager.cache.CacheObject;
042import org.kepler.objectmanager.cache.CacheObjectInterface;
043import org.kepler.objectmanager.lsid.KeplerLSID;
044
045import ptolemy.actor.TypedCompositeActor;
046import ptolemy.actor.gui.Configuration;
047import ptolemy.actor.gui.ModelDirectory;
048import ptolemy.actor.gui.PtolemyEffigy;
049import ptolemy.actor.gui.Tableau;
050import ptolemy.actor.gui.TableauFrame;
051import ptolemy.kernel.CompositeEntity;
052import ptolemy.kernel.util.IllegalActionException;
053import ptolemy.kernel.util.NameDuplicationException;
054import ptolemy.kernel.util.NamedObj;
055import ptolemy.moml.MoMLParser;
056
057/**
058 * @author Aaron Schultz
059 */
060public class ActorMetadataKAREntryHandler implements KAREntryHandler {
061
062        private static final Log log = LogFactory
063                        .getLog(ActorMetadataKAREntryHandler.class.getName());
064        private static final boolean isDebugging = log.isDebugEnabled();
065
066        // backwards compatibility type string
067        // KAR version 2.0 and 2.1 uses the binary class name as the type
068        public static final String TYPE = "actorMetadata";
069
070        // The class types that we'll handle with this KAREntryHandler
071        private static Vector<Class> handledClassTypes;
072
073        static {
074                handledClassTypes = new Vector<Class>(5);
075
076                try {
077                        handledClassTypes.add(Class
078                                        .forName("ptolemy.kernel.ComponentEntity"));
079                        handledClassTypes
080                                        .add(Class
081                                                        .forName("org.kepler.objectmanager.cache.ActorCacheObject"));
082                        handledClassTypes.add(Class
083                                        .forName("ptolemy.actor.TypedAtomicActor"));
084                        handledClassTypes.add(Class
085                                        .forName("ptolemy.actor.TypedCompositeActor"));
086                        handledClassTypes.add(Class
087                                        .forName("ptolemy.kernel.CompositeEntity"));
088                        handledClassTypes.add(Class
089                                        .forName("org.kepler.moml.CompositeClassEntity"));
090                } catch (ClassNotFoundException e) {
091                        e.printStackTrace();
092                }
093        }
094
095        public ActorMetadataKAREntryHandler() {
096
097        }
098
099        /**
100         * Method for backwards compatibility with KAR version 1.0 KAR version 2.0
101         * and 2.1 uses the binary class name as the type.
102         * 
103         * @see org.kepler.kar.KAREntryHandler#getTypeName()
104         */
105        public String getTypeName() {
106                return TYPE;
107        }
108
109        /**
110         * If the typeName is equal to or a subclass of any of the handled class
111         * types, then we return true.
112         */
113        public boolean handlesType(String typeName) {
114                return handlesClass(typeName);
115        }
116
117        /**
118         * If the typeName is equal to or a subclass of any of the handled class
119         * types, then we return true.
120         */
121        public static boolean handlesClass(String className) {
122
123                Class clazz;
124                try {
125                        clazz = Class.forName(className);
126                } catch (ClassNotFoundException e) {
127                        // e.printStackTrace();
128                        return false;
129                }
130
131                if (handledClassTypes.contains(clazz)) {
132                        return true;
133                }
134
135                // check superclasses
136                Class superClazz = clazz.getSuperclass();
137                while (superClazz != null) {
138                        if (handledClassTypes.contains(superClazz)) {
139                                return true;
140                        }
141                        superClazz = superClazz.getSuperclass();
142                }
143                return false;
144
145        }
146
147        /*
148         * (non-Javadoc)
149         * 
150         * @see org.kepler.kar.KAREntryHandler#initialize()
151         */
152        public void initialize() {
153                if (isDebugging) {
154                        log.debug("initialize()");
155                }
156        }
157        
158        /*
159        private static class KarEntryReference {
160                public KarEntryReference(KARFile karFile, KAREntry karEntry) {
161                        this.karFile = karFile.getFileLocation();
162                        this.karEntryString = karEntry.getName();
163                }
164
165                @Override
166                public boolean equals(Object o) {
167                        if (this == o) return true;
168                        if (o == null || getClass() != o.getClass()) return false;
169
170                        KarEntryReference that = (KarEntryReference) o;
171
172                        if (karEntryString != null ? !karEntryString.equals(that.karEntryString) : that.karEntryString != null)
173                                return false;
174                        if (karFile != null ? !karFile.equals(that.karFile) : that.karFile != null) return false;
175
176                        return true;
177                }
178
179                @Override
180                public int hashCode() {
181                        int result = karFile != null ? karFile.hashCode() : 0;
182                        result = 31 * result + (karEntryString != null ? karEntryString.hashCode() : 0);
183                        return result;
184                }
185
186                private final File karFile;
187                private String karEntryString;
188        }
189        */
190        
191        //private static Map<KarEntryReference, ActorCacheObject> acoCache = new HashMap<KarEntryReference, ActorCacheObject>();
192        
193        /*
194         * (non-Javadoc)
195         * 
196         * @see org.kepler.kar.KAREntryHandler#cache(org.kepler.kar.KARFile,
197         * java.util.jar.JarEntry)
198         */
199        public synchronized CacheObject cache(KARFile karFile, KAREntry entry) throws Exception {
200                if (isDebugging) {
201                        log.debug("cache(" + karFile.toString() + "," + entry.toString()
202                                        + ")");
203                }
204                // System.out.println("caching from amkeh" + karFile.toString() + "," +
205                // entry.toString());
206
207                // NOTE: the hashCode for KarEntryReference relies on the LSID.
208                // Sometimes the entry contents change, but not the LSID, e.g.,
209                // when the Display actor's window moves the LSID revision is not
210                // incremented. This results in the stale entry being found in
211                // acoCache and the updated entry not being updated in the cache.
212                /* 
213                KarEntryReference ker = new KarEntryReference(karFile, entry);
214                if (acoCache.containsKey(ker)) {
215                        return acoCache.get(ker);
216                }
217                */
218                                
219                ActorCacheObject aco = new ActorCacheObject(karFile
220                                .getInputStream(entry));
221                //acoCache.put(ker, aco);
222
223                if (isDebugging) {
224                        log.debug("created actor cache object: " + aco);
225                }
226                return aco;
227
228        }
229
230        /*
231         * (non-Javadoc)
232         * 
233         * @see org.kepler.kar.KAREntryHandler#open(java.util.jar.JarEntry)
234         */
235        public boolean open(KARFile karFile, KAREntry entry, TableauFrame tableauFrame) throws Exception {
236                if (isDebugging) {
237                        log.debug("open(" + karFile.toString() + "," + entry.toString()
238                                        + ")");
239                }
240
241                KeplerLSID lsid = entry.getLSID();
242                if (isDebugging)
243                        log.debug(lsid);
244
245                CacheManager cache = CacheManager.getInstance();
246
247                // get the object from the cache (it is GraphicalActorMetadata even
248                // though it is a workflow)
249                CacheObjectInterface co = cache.getObject(lsid);
250                NamedObj entity = null;
251
252                if (co == null) {
253                        if (isDebugging)
254                                log.debug(lsid + " was not found in the cache");
255                        if (isDebugging)
256                                log.debug("Opening from file");
257
258                        MoMLParser parser = new MoMLParser();
259                        InputStream stream = null;
260                        try {
261                            stream = karFile.getInputStream(entry);
262                            entity = parser.parse(null, karFile.getFileLocation().getCanonicalPath(), stream);
263                        } finally {
264                            if(stream != null) {
265                                stream.close();
266                            }
267                        }
268
269                        if (entity == null) {
270                                return false;
271                        }
272                } else {
273                        Object o = co.getObject();
274                        if (o == null) {
275                                return false;
276                        }
277                        if (isDebugging)
278                                log.debug(o.getClass().getName());
279
280                        if (o instanceof ActorMetadata) {
281                                ActorMetadata am = (ActorMetadata) o;
282                                try {
283                                        // get the workflow from the metadata
284                                        entity = am.getActorAsNamedObj(null);
285
286                                        if (isDebugging)
287                                                log.debug(entity.getName() + " "
288                                                                + NamedObjId.getIdFor(entity) + " "
289                                                                + entity.getClass().getName());
290                                        if (entity instanceof CompositeEntity
291                                                        || entity instanceof TypedCompositeActor) {
292                                                if (isDebugging)
293                                                        log.debug("Opening CompositeEntity");
294
295                                                // get the xml representation - needs parsing to be
296                                                // correct!
297                                                String moml = entity.exportMoML();
298                                                MoMLParser parser = new MoMLParser();
299                                                entity = parser.parse(moml);
300                                        } else {
301                                                if (isDebugging)
302                                                        log.debug("Not a CompositeEntity");
303                                                return false;
304                                        }
305                                } catch (Exception e) {
306                                        e.printStackTrace();
307                                        log.error("error opening the workflow: " + e.getMessage());
308                                        return false;
309                                }
310                        }
311                }
312
313                Configuration configuration = (Configuration) Configuration
314                                .configurations().iterator().next();
315
316                // TODO check on this
317                // ----begin questionable title bar code:
318                PtolemyEffigy effigy = new PtolemyEffigy(configuration.workspace());
319                effigy.setModel(entity);
320                ModelDirectory directory = (ModelDirectory) configuration
321                                .getEntity("directory");
322                
323                // is this the right name for the effigy?
324                effigy.setName(entity.getName());
325                
326                effigy.identifier.setExpression(entity.getName());
327                if (directory != null) {
328                        if (directory.getEntity(entity.getName()) != null) {
329                                // Name is already taken.
330                                int count = 2;
331                                String newName = effigy.getName() + " " + count;
332                                while (directory.getEntity(newName) != null) {
333                                        newName = effigy.getName() + " " + ++count;
334                                }
335                                effigy.setName(newName);
336                        }
337                }
338                effigy.setContainer(directory);
339                // ---end questionable title bar code
340
341                // open a new window for the workflow
342                Tableau t = configuration.openModel(entity);
343                
344                if (t != null) {
345                        //if (isDebugging)
346                                //log.debug(entity.getName() + " was opened successfully");
347
348                        // no need to put this in ObjectManager for ReportLayoutKAREntry's
349                        // open,
350                        // it gets in there somehow anyways
351                        // ObjectManager.getInstance().addNamedObj(entity);
352                        // ObjectManager.assignIdTo(entity, lsid);
353
354                        //Add JFrame => KARFile mapping to KARManager
355                        KARManager karManager = KARManager.getInstance();
356                        karManager.add(t.getFrame(), karFile);
357                        
358                        return true;
359                }
360
361                return false;
362
363        }
364
365        /**
366         * The save method for ActorMetadataKAREntryHandler is not used since Actors
367         * and Workflows are not dependent on anything else. They are the top of the
368         * dependency tree for KARs and are therefore created by the
369         * KARBuilder.handleInitiatorList Method. However, the
370         * ActorMetadataKAREntryHandler is still listed as the handler that created
371         * the file in the KAR manifest since this handler is used for opening and
372         * cacheing.
373         * 
374         * @see org.kepler.kar.KAREntryHandler#save(org.kepler.objectmanager.lsid.KeplerLSID
375         *      )
376         */
377        public Hashtable<KAREntry, InputStream> save(Vector<KeplerLSID> lsids,
378                        KeplerLSID karLsid, TableauFrame tableauFrame) throws Exception {
379                if (isDebugging)
380                        log.debug("save(" + lsids + ")");
381
382                return null;
383        }
384
385        /**
386         * A factory that creates a KAREntryHandler object.
387         * 
388         *@author Aaron Schultz
389         */
390        public static class Factory extends KAREntryHandlerFactory {
391                /**
392                 * Create a factory with the given name and container.
393                 * 
394                 *@param container
395                 *            The container.
396                 *@param name
397                 *            The name of the entity.
398                 *@exception IllegalActionException
399                 *                If the container is incompatible with this attribute.
400                 *@exception NameDuplicationException
401                 *                If the name coincides with an attribute already in the
402                 *                container.
403                 */
404                public Factory(NamedObj container, String name)
405                                throws IllegalActionException, NameDuplicationException {
406                        super(container, name);
407                }
408
409                /**
410                 * Create a library pane that displays the given library of actors.
411                 * 
412                 * @return A new LibraryPaneTab that displays the library
413                 */
414                public KAREntryHandler createKAREntryHandler() {
415                        if (isDebugging)
416                                log.debug("createKAREntryHandler()");
417                        ActorMetadataKAREntryHandler amkeh = new ActorMetadataKAREntryHandler();
418                        return amkeh;
419                }
420        }
421}