001/*
002 * Copyright (c) 1998-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2012-06-28 21:28:16 +0000 (Thu, 28 Jun 2012) $' 
007 * '$Revision: 30086 $'
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.frame;
031
032import java.awt.Color;
033import java.lang.ref.WeakReference;
034import java.util.List;
035import java.util.Vector;
036
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039import org.kepler.gui.KeplerGraphFrame;
040import org.kepler.gui.kar.KAREffigy;
041import org.kepler.kar.KARManager;
042import org.kepler.moml.NamedObjId;
043import org.kepler.moml.NamedObjIdReferralList;
044import org.kepler.objectmanager.ObjectManager;
045import org.kepler.objectmanager.lsid.KeplerLSID;
046import org.kepler.util.TransientStringAttribute;
047
048import ptolemy.actor.gui.Effigy;
049import ptolemy.actor.gui.PtolemyEffigy;
050import ptolemy.actor.gui.Tableau;
051import ptolemy.actor.gui.TableauFactory;
052import ptolemy.actor.gui.TableauFrame;
053import ptolemy.kernel.CompositeEntity;
054import ptolemy.kernel.InstantiableNamedObj;
055import ptolemy.kernel.util.Attribute;
056import ptolemy.kernel.util.IllegalActionException;
057import ptolemy.kernel.util.InternalErrorException;
058import ptolemy.kernel.util.NameDuplicationException;
059import ptolemy.kernel.util.NamedObj;
060import ptolemy.kernel.util.StringAttribute;
061import ptolemy.kernel.util.Workspace;
062import ptolemy.moml.LibraryAttribute;
063
064//////////////////////////////////////////////////////////////////////////
065//// Kepler GraphTableau
066
067/**
068 A graph editor for ptolemy models.  
069
070 This is a graph editor for ptolemy models.  It constructs an instance
071 of GraphFrame, which contains an editor pane based on diva.
072
073 @see ptolemy.vergil.actor.ActorGraphTableau
074 @author  Based on GraphTableau by Steve Neuendorffer and Edward A. Lee
075 @version $Id: KeplerGraphTableau.java 30086 2012-06-28 21:28:16Z crawl $
076 @since Ptolemy II 2.0
077 @Pt.ProposedRating Red (neuendor)
078 @Pt.AcceptedRating Red (johnr)
079 */
080public class KeplerGraphTableau extends Tableau {
081        private static final Log log = LogFactory.getLog(KeplerGraphTableau.class
082                        .getName());
083        private static final boolean isDebugging = log.isDebugEnabled();
084        
085    // This class extends Tableau so that highlighting of actors
086    // works.  See
087    // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=2321
088    // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4050
089    // util/src/org/kepler/sms/gui/WorkflowTypeCheckerDialog.java
090    // ptII/ptolemy/vergil/unit/UnitSolverDialog.java
091
092    // The only substantive difference between this class and
093    // ptolemy.vergil.actor.ActorGraphTableau is that this class
094    // constructs an instance of KeplerGraphFrame.
095    
096        /**
097         * Create a tableau in the specified workspace.
098         * 
099         * @param workspace
100         *            The workspace.
101         * @exception IllegalActionException
102         *                If thrown by the superclass.
103         * @exception NameDuplicationException
104         *                If thrown by the superclass.
105         */
106        public KeplerGraphTableau(Workspace workspace)
107                        throws IllegalActionException, NameDuplicationException {
108                super(workspace);
109                if (isDebugging) {
110                        log.debug("KeplerGraphTableau(workspace)");
111                }
112        }
113
114        /**
115         * Create a tableau with the specified container and name, with no specified
116         * default library.
117         * 
118         * @param container
119         *            The container.
120         * @param name
121         *            The name.
122         * @exception IllegalActionException
123         *                If thrown by the superclass.
124         * @exception NameDuplicationException
125         *                If thrown by the superclass.
126         */
127        public KeplerGraphTableau(PtolemyEffigy container, String name)
128                        throws IllegalActionException, NameDuplicationException {
129                this(container, name, null);
130        }
131
132        /**
133         * Create a tableau with the specified container, name, and default library.
134         * 
135         * @param container
136         *            The container.
137         * @param name
138         *            The name.
139         * @param defaultLibrary
140         *            The default library, or null to not specify one.
141         * @exception IllegalActionException
142         *                If thrown by the superclass.
143         * @exception NameDuplicationException
144         *                If thrown by the superclass.
145         */
146        public KeplerGraphTableau(PtolemyEffigy container, String name,
147                        LibraryAttribute defaultLibrary) throws IllegalActionException,
148                        NameDuplicationException {
149                super(container, name);
150                if (isDebugging) {
151                        log.debug("KeplerGraphTableau");
152                        if (container == null) {
153                                log.debug("container is null");
154                        } else {
155                                log.debug(container.getName() + " : " + container.getClassName());
156                        }
157                        log.debug("name: " + name);
158                }
159
160                NamedObj model = container.getModel();
161
162                if (model == null) {
163                        return;
164                }
165
166                if (!(model instanceof CompositeEntity)) {
167                        throw new IllegalActionException(this,
168                                        "Cannot graphically edit a model "
169                                                        + "that is not a CompositeEntity. Model is a "
170                                                        + model);
171                }
172                
173                InstantiableNamedObj inoModel = (InstantiableNamedObj) model;
174                boolean isClass = inoModel.isClassDefinition();
175                //System.out.println("isClass: " + isClass);
176                //System.out.println("model class name: " + inoModel.getClassName());
177                
178                ObjectManager om = ObjectManager.getInstance();
179                
180                if (isClass) {
181                        // This is a class so assign a new id to it
182                        NamedObjId noid = NamedObjId.getIdAttributeFor(model);
183                        if (noid == null) {
184                                // probably will always get this
185                                try {
186                                        NamedObjId.assignIdTo(model);
187                                } catch (Exception e) {
188                                        e.printStackTrace();
189                                }
190                        }
191                        
192                        // And figure out the class name
193                        if (model instanceof InstantiableNamedObj) {
194                                InstantiableNamedObj ino = (InstantiableNamedObj) model;
195                                List inoc = ino.getChildren();
196                                if (inoc != null) {
197                                        if (isDebugging) log.debug("has " + inoc.size() + " children");
198                                        if (inoc.size() > 0) {
199                                                Vector<NamedObj> noChildren = new Vector<NamedObj>();
200                                                for (Object o : inoc) {
201                                                        if (isDebugging) log.debug(o.getClass().getName());
202                                                        if (o instanceof WeakReference) {
203                                                                WeakReference w = (WeakReference) o;
204                                                                Object o2 = w.get();
205                                                                if (isDebugging) log.debug(o2.getClass().getName());
206                                                                if (o2 instanceof NamedObj) {
207                                                                        noChildren.add((NamedObj) o2);
208                                                                }
209                                                        } else if (o instanceof NamedObj) {
210                                                                noChildren.add( (NamedObj) o );
211                                                        }
212                                                }
213                                                NamedObj theChild = null;
214                                                Vector<NamedObj> noChildrenWithClass = new Vector<NamedObj>();
215                                                for (NamedObj aChild : noChildren) {
216                                                        Attribute classAtt = aChild.getAttribute("class");
217                                                        if (classAtt != null) {
218                                                                StringAttribute classAttribute = (StringAttribute) classAtt;
219                                                                String className = classAttribute.getExpression();
220                                                                noChildrenWithClass.add(aChild);
221                                                                //System.out.println("class name: " + className);
222                                                        }
223                                                }
224                                                if (noChildrenWithClass.size() == 1) {
225                                                        theChild = noChildrenWithClass.get(0);
226                                                }
227                                                if (theChild != null) {
228                                                        // Tag this object with the classname from the child so it can be saved properly
229                                                        // See ActorMetadata(NamedObj)
230                                                        Attribute classAtt = theChild.getAttribute("class");
231                                                        if (classAtt != null) {
232                                                                Attribute tcn = model.getAttribute("tempClassName");
233                                                                if (tcn == null) {
234                                                                        StringAttribute classAttribute = (StringAttribute) classAtt;
235                                                                        String className = classAttribute.getExpression();
236                                                                        TransientStringAttribute newClassAttribute = new TransientStringAttribute(model,"tempClassName");
237                                                                        newClassAttribute.setExpression(className);
238                                                                        Attribute idAtt = classAttribute.getAttribute("id");
239                                                                        if (idAtt != null) {
240                                                                                String classId = ((StringAttribute) idAtt).getExpression();
241                                                                                TransientStringAttribute newClassIdAttribute = new TransientStringAttribute(newClassAttribute,"id");
242                                                                                newClassIdAttribute.setExpression(classId);
243                                                                        }
244                                                                }
245                                                        }
246                                                        // Also, populate the Referral list with the child's Id, and set the parent
247                                                        NamedObjId childId = NamedObjId.getIdAttributeFor(theChild);
248                                                        if (childId != null) {
249                                                                NamedObjId newNoId = NamedObjId.getIdAttributeFor(model);
250                                                                try {
251                                                                        //NamedObjId.assignIdTo(model);
252                                                                        NamedObjIdReferralList childIdList = NamedObjId.getIDListAttributeFor(model);
253                                                                        if (childIdList == null) {
254                                                                                childIdList = new NamedObjIdReferralList(model,NamedObjIdReferralList.NAME);
255                                                                        }
256                                                                        KeplerLSID childLsid = childId.getId();
257                                                                        boolean alreadyInList = childIdList.hasReferral(childLsid);
258                                                                        boolean matchesLsid = childLsid.equals(newNoId.getId());
259                                                                        if (!alreadyInList && !matchesLsid) {
260                                                                                String childLsidStr = childLsid.toString();
261                                                                                KeplerLSID newId = new KeplerLSID(childLsidStr);
262                                                                                childIdList.addReferral(newId);
263                                                                        }
264                                                                } catch (Exception e) {
265                                                                        e.printStackTrace();
266                                                                }
267                                                                // Also, we need to set the child id as the parent of the new id
268                                                                // This allows the id to be updated when the new opened actor is edited.
269                                                                try {
270                                                                        NamedObjId.assignIdTo(theChild, newNoId.getId());
271                                                                } catch (Exception e) {
272                                                                        e.printStackTrace();
273                                                                }
274                                                                newNoId.setParentId(NamedObjId.getIdAttributeFor(theChild));
275                                                        }
276                                                        
277                                                }
278                                        }
279                                }
280                        }
281                        
282
283                } else {
284                        // Check to see if this model has a KeplerLSID associated with it
285                        try {
286                                NamedObjId noid = NamedObjId.getIdAttributeFor(model);
287                                if (noid == null) {
288                                        // if there is no KeplerLSID we'll check to see if this model
289                                        // was cloned from a child through instantiation, if it was
290                                        // we will assign the child's id to this model
291                                        if (model instanceof InstantiableNamedObj) {
292                                                InstantiableNamedObj ino = (InstantiableNamedObj) model;
293                                                List inoc = ino.getChildren();
294                                                if (inoc != null) {
295                                                        if (isDebugging) log.debug("has " + inoc.size() + " children");
296                                                        if (inoc.size() > 0) {
297                                                                Vector<NamedObj> noChildren = new Vector<NamedObj>();
298                                                                for (Object o : inoc) {
299                                                                        if (isDebugging) log.debug(o.getClass().getName());
300                                                                        if (o instanceof WeakReference) {
301                                                                                WeakReference w = (WeakReference) o;
302                                                                                Object o2 = w.get();
303                                                                                if (isDebugging) log.debug(o2.getClass().getName());
304                                                                                if (o2 instanceof NamedObj) {
305                                                                                        noChildren.add((NamedObj) o2);
306                                                                                }
307                                                                        } else if (o instanceof NamedObj) {
308                                                                                noChildren.add( (NamedObj) o );
309                                                                        }
310                                                                }
311                                                                NamedObj theChild = null;
312                                                                Vector<NamedObj> noChildrenWithIds = new Vector<NamedObj>();
313                                                                for (NamedObj aChild : noChildren) {
314                                                                        NamedObjId aNoi = NamedObjId.getIdAttributeFor(aChild);
315                                                                        if (aNoi != null) {
316                                                                                noChildrenWithIds.add(aChild);
317                                                                        }
318                                                                }
319                                                                if (noChildrenWithIds.size() == 1) {
320                                                                        theChild = noChildrenWithIds.get(0);
321                                                                }
322                                                                if (theChild != null) { 
323                                                                        if (isDebugging) log.debug("child: "+theChild.getName() + " : " + theChild.getClassName());
324                                                                        /*
325                                                                        System.out.println("***************************************************************");
326                                                                        System.out.println(model.exportMoML());
327                                                                        System.out.println("***************************************************************");
328                                                                        System.out.println(no.exportMoML());
329                                                                        System.out.println("***************************************************************");
330                                                                        */
331                                                                        NamedObjId theChildId = NamedObjId.getIdAttributeFor(theChild);
332                                                                        if (theChildId != null) {
333                                                                                noid = theChildId;
334                                                                        }
335                                                                }
336                                                        }
337                                                } else {
338                                                        log.debug("instantiable model has no children");
339                                                }
340                                        }
341                                }
342                                if (noid == null) {
343                                        NamedObjId.assignIdTo(model);
344                                } else {
345                                        NamedObjId.assignIdTo(model,noid.getId());
346                                }
347                                if (isDebugging) {
348                                        NamedObjId tnoid = NamedObjId.getIdAttributeFor(model);
349                                        if (tnoid == null) {
350                                                log.debug("NamedObjId is still null");
351                                        } else {
352                                                log.debug(tnoid.getId().toString());
353                                        }
354                                }
355                        } catch (Exception e) {
356                            throw new IllegalActionException(model, e,
357                                                "No Kepler ID was or could be assigned to model: "
358                                                                + e.toString());
359                        }
360                }
361
362                CompositeEntity entity = (CompositeEntity) model;
363                try {
364                        om.addNamedObj(entity);
365                } catch (Exception e1) {
366                        e1.printStackTrace();
367                }
368
369                createGraphFrame(entity, defaultLibrary);
370                
371                if(container instanceof KAREffigy) {
372                        KAREffigy effigy = (KAREffigy) container;
373                        // associate the frame
374                        KARManager.getInstance().add(getFrame(), effigy.getKARFile());
375                        
376                        // if it's a kar, open the non-actor entries, e.g., report layout
377                        effigy.openKAREntries((TableauFrame) getFrame(), false);
378                }
379                
380        }
381        
382    /** Create the graph frame that displays the model associated with
383     *  this tableau together with the specified library.
384     *  @param model The Ptolemy II model to display in the graph frame.
385     *  @param defaultLibrary The default library, or null to not specify
386     *   one.
387     */
388     public void createGraphFrame(CompositeEntity model,
389            LibraryAttribute defaultLibrary)
390     {
391        KeplerGraphFrame frame = new KeplerGraphFrame(model, this,
392                defaultLibrary);
393
394        try {
395            setFrame(frame);
396        } catch (IllegalActionException e) {
397            throw new InternalErrorException(e);
398        }
399        frame.setBackground(BACKGROUND_COLOR);
400    }
401
402        // /////////////////////////////////////////////////////////////////
403        // // private members ////
404        // The background color.
405        private static Color BACKGROUND_COLOR = new Color(0xe5e5e5);
406
407        // /////////////////////////////////////////////////////////////////
408        // // public inner classes ////
409
410        /**
411         * A factory that creates graph editing tableaux for Ptolemy models.
412         */
413        public static class Factory extends TableauFactory {
414                /**
415                 * Create an factory with the given name and container.
416                 * 
417                 * @param container
418                 *            The container.
419                 * @param name
420                 *            The name.
421                 * @exception IllegalActionException
422                 *                If the container is incompatible with this attribute.
423                 * @exception NameDuplicationException
424                 *                If the name coincides with an attribute already in the
425                 *                container.
426                 */
427                public Factory(NamedObj container, String name)
428                                throws IllegalActionException, NameDuplicationException {
429                        super(container, name);
430                }
431
432                /**
433                 * Create a tableau in the default workspace with no name for the given
434                 * Effigy. The tableau will created with a new unique name in the given
435                 * model effigy. If this factory cannot create a tableau for the given
436                 * effigy (perhaps because the effigy is not of the appropriate
437                 * subclass) then return null. It is the responsibility of callers of
438                 * this method to check the return value and call show().
439                 * 
440                 * @param effigy
441                 *            The model effigy.
442                 * @return A new KeplerGraphTableau, if the effigy is a PtolemyEffigy,
443                 *         or null otherwise.
444                 * @exception Exception
445                 *                If an exception occurs when creating the tableau.
446                 */
447                public Tableau createTableau(Effigy effigy) throws Exception {
448                        if (effigy instanceof PtolemyEffigy) {
449                                // First see whether the effigy already contains a graphTableau.
450                                KeplerGraphTableau tableau = (KeplerGraphTableau) effigy
451                                                .getEntity("graphTableau");
452
453                                if (tableau == null) {
454                                        // Check to see whether this factory contains a
455                                        // default library.
456                                        LibraryAttribute library = (LibraryAttribute) getAttribute(
457                                                        "_library", LibraryAttribute.class);
458                                        tableau = new KeplerGraphTableau((PtolemyEffigy) effigy,
459                                                        "graphTableau", library);
460                                }
461
462                                // Don't call show() here, it is called for us in
463                                // TableauFrame.ViewMenuListener.actionPerformed()
464                                return tableau;
465                        } else {
466                                return null;
467                        }
468                }
469        }
470}