001/*
002 * Copyright (c) 2003-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-08-17 01:21:47 +0000 (Mon, 17 Aug 2015) $' 
007 * '$Revision: 33574 $'
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.moml;
031
032import java.util.Collection;
033import java.util.Iterator;
034import java.util.List;
035import java.util.Vector;
036
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039import org.kepler.objectmanager.lsid.KeplerLSID;
040import org.kepler.objectmanager.lsid.LSIDGenerator;
041
042import ptolemy.actor.gui.RenameConfigurer;
043import ptolemy.kernel.undo.RedoChangeRequest;
044import ptolemy.kernel.undo.UndoChangeRequest;
045import ptolemy.kernel.util.Attribute;
046import ptolemy.kernel.util.ChangeListener;
047import ptolemy.kernel.util.ChangeRequest;
048import ptolemy.kernel.util.IllegalActionException;
049import ptolemy.kernel.util.NameDuplicationException;
050import ptolemy.kernel.util.NamedObj;
051import ptolemy.kernel.util.Settable;
052import ptolemy.kernel.util.StringAttribute;
053import ptolemy.kernel.util.ValueListener;
054import ptolemy.kernel.util.Workspace;
055import ptolemy.moml.MoMLChangeRequest;
056import ptolemy.util.MessageHandler;
057import ptolemy.vergil.actor.ActorGraphModel;
058import ptolemy.vergil.basic.LocatableNodeDragInteractor;
059
060/**
061 * The NamedObjId is a StringAttribute to be used for identifying a NamedObj by
062 * a KeplerLSID. A NamedObj that contains this Attribute is considered to be
063 * uniquely identified by the KeplerLSID that this NamedObjId stores. This class
064 * also handles the updating of KeplerLSIDs based on the ptolemy change listener
065 * system. Static methods in org.kepler.objectmanager.ObjectManager can be used
066 * to access NamedObjId attributes contained by NamedObjs.
067 * 
068 *@author berkley, schultz
069 *@created March 1, 2005
070 */
071public class NamedObjId extends StringAttribute implements ChangeListener {
072        private static final long serialVersionUID = 3665319359405551002L;
073        private static final Log log = LogFactory
074                        .getLog(NamedObjId.class.getName());
075        private static final boolean isDebugging = log.isDebugEnabled();
076        
077        /** If true, new NamedObjId objects will increment the LSID when changes
078         *  to the workflow occur.
079         */
080    private static boolean _incrementLSIDOnWorkflowChange = true;
081
082        /** container for valueListeners assigned to this attribute */
083        private Vector<ValueListener> valueListeners = new Vector<ValueListener>();
084
085        private KeplerLSID _id = null;
086
087        public static final String NAME = "entityId";
088        
089        /* A parent NamedObjId for this NamedObjId
090         * The parent will always be updated when this one is.
091         */
092        private NamedObjId _parentId = null;
093
094        public NamedObjId getParentId() {
095                return _parentId;
096        }
097
098        public void setParentId(NamedObjId parent) {
099                _parentId = parent;
100        }
101
102        /** Constructor */
103        public NamedObjId() {
104                super();
105                if(_incrementLSIDOnWorkflowChange) {
106                    addChangeListener(this);
107                }
108        }
109
110        /**
111         * Constructor
112         * 
113         *@param container
114         *            Description of the Parameter
115         *@param name
116         *            Description of the Parameter
117         *@exception IllegalActionException
118         *                Description of the Exception
119         *@exception NameDuplicationException
120         *                Description of the Exception
121         */
122        public NamedObjId(NamedObj container, String name)
123                        throws IllegalActionException, NameDuplicationException {
124                super(container, name);
125        if(_incrementLSIDOnWorkflowChange) {
126            addChangeListener(this);
127        }
128        }
129
130        /**
131         * Constructor
132         * 
133         *@param workspace
134         *            Description of the Parameter
135         */
136        public NamedObjId(Workspace workspace) {
137                super(workspace);
138        if(_incrementLSIDOnWorkflowChange) {
139            addChangeListener(this);
140        }
141        }
142        
143        /**
144         * Generate a new KeplerLSID and assign it to this NamedObj.
145         * 
146         * @param no
147         *            the NamedObj.
148         * @throws Exception
149         */
150        public static void assignIdTo(NamedObj no) throws Exception {
151                assignIdTo(no, LSIDGenerator.getInstance().getNewLSID(), true);
152        }
153
154        /**
155         * Assign the given KeplerLSID to this NamedObj. If the NamedObj already has
156         * this LSID assigned to it do nothing. If the NamedObj has a different
157         * LSID, assign the new LSID to the NamedObj and move the old LSID into the
158         * referral list.
159         * 
160         * @param no
161         * @param lsid
162         * @throws Exception
163         */
164        public static void assignIdTo(NamedObj no, KeplerLSID lsid)
165                        throws Exception {
166                assignIdTo(no, lsid, true);
167        }
168
169        /**
170         * Assign the given KeplerLSID to this NamedObj. If the NamedObj already has
171         * this LSID assigned to it do nothing. If the NamedObj has a different
172         * LSID, assign the new LSID to the NamedObj and optionally move the old
173         * LSID into the referral list.
174         * 
175         * @param no
176         *            the NamedObj
177         * @param lsid
178         *            the new LSID to assign.
179         * @param updateReferrals
180         *            if true, update the referrals list.
181         * @throws Exception
182         */
183        public static void assignIdTo(NamedObj no, KeplerLSID lsid,
184                        boolean updateReferrals) throws Exception {
185                if (isDebugging)
186                        log.debug("assignIdTo(" + no.getName() + ", " + lsid + ")");
187
188                NamedObjIdReferralList referrals = null;
189
190                if (updateReferrals) {
191                        // make sure there is a referral list
192                        Attribute referralListAttribute = no
193                                        .getAttribute(NamedObjIdReferralList.NAME);
194                        // if it does not exist, create one
195                        if (referralListAttribute == null) {
196                                referrals = new NamedObjIdReferralList(no,
197                                                NamedObjIdReferralList.NAME);
198                        // make sure the attribute is a NamedObjIdReferralList
199                        } else if(referralListAttribute instanceof NamedObjIdReferralList) {
200                                referrals = (NamedObjIdReferralList) referralListAttribute;
201                        } else if(referralListAttribute instanceof Settable) {
202                                // the attribute is not a NamedObjIdReferralList
203                                // this can happen if a Kepler workflow is opened in Ptolemy, saved,
204                                // then opened in Kepler. (Ptolemy does not have NamedObjIdReferralList,
205                                // so converts to StringAttribute).
206                                
207                                // copy the contents, remove it, and replace with a NamedObjIdReferralList.
208                                String expression = ((Settable) referralListAttribute).getExpression();
209                                no.removeAttribute(referralListAttribute);
210                                referrals = new NamedObjIdReferralList(no,
211                                                NamedObjIdReferralList.NAME);
212                                referrals.setExpression(expression);
213                        }
214                }
215
216                // find the entityId attribute
217                NamedObjId lsidAtt = null;
218                try {
219                    // If the user opens a Kepler-2.2 model in Ptolemy and then
220                    // saves it, the org.kepler.moml.NamedObjId
221                    // will be replaced with a StringAttribute.  Then, if the user
222                    // tries to reopen the model in Kepler, there will be a cast
223                    // exception.  So, we get rid of the offending attribute and
224                    // create another.
225                    lsidAtt = (NamedObjId) no.getAttribute(NamedObjId.NAME);
226                } catch (ClassCastException ex) {
227                    no.getAttribute(NamedObjId.NAME).setContainer(null);
228                    lsidAtt = null;
229                }
230                if (lsidAtt == null) {
231                        if (isDebugging)
232                                log.debug("lsidAtt is null");
233
234                        lsidAtt = new NamedObjId(no, NamedObjId.NAME);
235                        lsidAtt.setExpression(lsid.toString());
236
237                        // look for the ID in the Annotation attribute. (for backwards
238                        // compatibility)
239                        Attribute a = no.getAttribute("Annotation");
240                        if (a != null) {
241                                if (isDebugging)
242                                        log.debug("a not null");
243                                NamedObjId annoLsid = (NamedObjId) a
244                                                .getAttribute(NamedObjId.NAME);
245                                if (annoLsid != null) {
246                                        if (isDebugging)
247                                                log.debug("found lsidAtt in Annotation");
248                                        lsidAtt.setExpression(annoLsid.getExpression());
249                                        annoLsid.setContainer(null);
250                                }
251                        }
252                }
253
254                // Add the old lsid to the derivedFrom list if it is a different
255                // object, namespace, or authority
256                if (lsidAtt.getId() != null) {
257                        if (!lsidAtt.getId().equalsWithoutRevision(lsid) && updateReferrals) {
258                                if (isDebugging)
259                                        log.debug("add referral");
260                                referrals.addReferral(lsidAtt.getId());
261                        }
262                }
263                lsidAtt.setExpression(lsid.toString());
264        }
265
266        /** Clone the object into the specified workspace. */
267        @Override
268    public Object clone(Workspace workspace) throws CloneNotSupportedException {
269            NamedObjId newObject = (NamedObjId) super.clone(workspace);
270            newObject._id = null;
271            newObject._parentId = null;
272            newObject.valueListeners = new Vector<ValueListener>();
273            return newObject;
274        }
275        
276        /**
277         * returns the default expression which is null
278         * 
279         *@return The defaultExpression value
280         */
281        @Override
282    public String getDefaultExpression() {
283                return null;
284        }
285
286        public KeplerLSID getId() {
287                return _id;
288        }
289        
290        /**
291         * Return the LSID for the given NamedObj. If an LSID has not already been
292         * assigned then assign the NamedObj a new LSID.
293         * 
294         * @param no
295         * */
296        public static KeplerLSID getIdFor(NamedObj no) {
297                NamedObjId lsidAtt = NamedObjId.getIdAttributeFor(no);
298                if (lsidAtt == null) {
299                        try {
300                                NamedObjId.assignIdTo(no);
301                                lsidAtt = NamedObjId.getIdAttributeFor(no);
302                        } catch (Exception e) {
303                                e.printStackTrace();
304                        }
305                }
306                String lsidStr = lsidAtt.getExpression();
307                if (lsidStr == null) {
308                        return null;
309                }
310                try {
311                        return new KeplerLSID(lsidStr);
312                } catch (Exception e) {
313                        return null;
314                }
315        }
316        
317        /**
318         * Return the KeplerIDListAttribute associated with the given NamedObj or
319         * null if no KeplerIDListAttribute exists.
320         * 
321         * @param no
322         *            * @throws NameDuplicationException
323         * @throws IllegalActionException
324         */
325        public static NamedObjIdReferralList getIDListAttributeFor(NamedObj no)
326                        throws IllegalActionException, NameDuplicationException {
327
328                NamedObjIdReferralList theAtt = null;
329
330                // check the attributes of this obj
331                // FIXME: why iterate over all the attributes instead of
332                // getting the attribute named NamedObjIdReferralList.NAME?
333                List<Attribute> theAtts = no.attributeList();
334                for (Iterator<Attribute> atit = theAtts.iterator(); atit.hasNext();) {
335                        Attribute anAtt = atit.next();
336                        if (isDebugging)
337                                log.debug(anAtt.getName() + " " + anAtt);
338                        log.debug(anAtt.getClass().getName());
339                        if (anAtt instanceof NamedObjIdReferralList) {
340                                theAtt = (NamedObjIdReferralList) anAtt;
341                                if (isDebugging)
342                                        log.debug("FOUND IT");
343                                return theAtt;
344                        }
345                }
346
347                if (theAtt == null) {
348                        theAtt = new NamedObjIdReferralList(no, NamedObjIdReferralList.NAME);
349                }
350
351                return theAtt;
352
353        }
354        
355        /**
356         * Return the NamedObjId associated with the given NamedObj or null if no
357         * associated NamedObjId.
358         * 
359         * @param no
360         * */
361        public static NamedObjId getIdAttributeFor(NamedObj no) {
362                if (no == null) return null;
363                
364                Attribute idAtt = no.getAttribute(NAME);
365                if (idAtt == null) {
366                        return null;
367                }
368                if (idAtt instanceof NamedObjId) {
369                        NamedObjId theAtt = (NamedObjId) idAtt;
370                        return theAtt;
371                }
372                return null;
373        }
374
375        /**
376         * set the value of this id.
377         * 
378         * If you want a NamedObjIdChangeRequest sent out in response to this
379         * setExpression you must call requestNamedObjIdChange(). updateRevision
380         * does this for you.
381         * 
382         *  @see #requestNamedObjIdChange()
383         * 
384         *@param expression
385         *            The new expression value
386         */
387        @Override
388    public void setExpression(String expression) throws IllegalActionException {
389                super.setExpression(expression);
390
391                try {
392                        _id = new KeplerLSID(expression);
393                } catch (Exception e) {
394                        _id = null;
395                        throw new IllegalActionException("KeplerID format is incorrect. "
396                                        + e.toString());
397                }
398                
399                /*
400                 * It seems like requestNamedObjIdChange() should be private and
401                 * called here, but unfortunately this kicks off an infinite 
402                 * during startup.
403                 * NamedObj => MoMLParser => NamedObj => ChangeRequest => NamedObj
404                 * => StringAttribute => NamedObjId.
405                 *
406                 *requestNamedObjIdChange();
407                */
408                
409                for (int i = 0; i < valueListeners.size(); i++) {
410                        // notify any listeners of the change
411                        ValueListener listener = valueListeners
412                                        .elementAt(i);
413                        listener.valueChanged(this);
414                }
415                
416                // Update the parent
417                NamedObjId parentId = getParentId();
418                if (parentId != null) {
419                        NamedObj parentsContainer = parentId.getContainer();
420                        if (parentsContainer != null) {
421                                try {
422                                        NamedObjId.assignIdTo(parentsContainer,getId());
423                                } catch (Exception e) {
424                                        e.printStackTrace();
425                                }
426                        }
427                }
428                
429        }
430
431        /**
432         * Helper method for setting the expression directly with a KeplerLSID
433         * object.
434         * 
435         * @param lsid
436         * @throws IllegalActionException
437         */
438        public void setExpression(KeplerLSID lsid) throws IllegalActionException {
439                this.setExpression(lsid.toString());
440        }
441
442        /**
443         * add a valueListener
444         * 
445         *@param listener
446         *            The feature to be added to the ValueListener attribute
447         */
448        @Override
449    public void addValueListener(ValueListener listener) {
450                valueListeners.add(listener);
451        }
452
453        /**
454         * NamedObjIds should be invisible to the user
455         * 
456         *@return The visibility value
457         */
458        @Override
459    public Settable.Visibility getVisibility() {
460                return NONE;
461        }
462
463        /**
464         * this method does not change the visibility. NamedObjId should only be
465         * invisible
466         * 
467         *@param visibility
468         *            The new visibility value
469         */
470        @Override
471    public void setVisibility(Settable.Visibility visibility) {
472                // do nothing....we don't want the visibility getting changed
473        }
474
475        /**
476         * remove the indicated listener
477         * 
478         *@param listener
479         *            Description of the Parameter
480         */
481        @Override
482    public void removeValueListener(ValueListener listener) {
483                valueListeners.remove(listener);
484        }
485
486        /**
487         * @param change
488         * @throws Exception
489         */
490        private void handleNamedObjIdChangeRequest(NamedObjIdChangeRequest change)
491                        throws Exception {
492
493                Object source = change.getSource();
494
495                if (source instanceof NamedObjId) {
496
497                        NamedObj context = change.getContext();
498                        NamedObj container = getContainer();
499                        if (container == null)
500                                throw new Exception(
501                                                "NamedObjId.getContainer() is null on NamedObjIdChangeRequest");
502
503                        if (container.deepContains(context)) {
504                                updateRevision();
505                        }
506
507                }
508        }
509
510        /**
511         * @param change
512         * @throws Exception
513         */
514        private void handleMoMLChangeRequest(MoMLChangeRequest change)
515                        throws Exception {
516
517                Object source = change.getSource();
518                NamedObj context = change.getContext();
519                NamedObj container = this.getContainer();
520
521                if (isDebugging) {
522                        if (context != null) {
523                                log.debug("MoMLChangeRequest.context " + context.getName()
524                                                + " " + context.getClass().getName());
525                        } else {
526                                log.debug("MoMLChangeRequest.context is null");
527                        }
528                        log.debug("context: " + context);
529                        log.debug("container: " + container);
530                }
531
532                if (source instanceof LocatableNodeDragInteractor) {
533                        if (isDebugging)
534                                log.debug("Handle LocatableNodeDragInteractor");
535
536                        NamedObj containerContainer = container.getContainer();
537                        if (containerContainer == null) {
538                                // This is a change in the location of a component on the
539                                // canvas and we are in the NamedObjId for the top level object
540                                // So roll the revision on the top level object since it's
541                                // xml has changed
542                                updateRevision();
543                        }
544
545                } else if (source instanceof ActorGraphModel) {
546                        if (isDebugging) {
547                                log.debug("Handle ActorGraphModel");
548                                log.debug(change.getClass().getName());
549                        }
550                        if (context == container) {
551
552                                // Because we cannot tell if the relation is being
553                                // started or ended here the lsid revision gets updated
554                                // both when the user starts the relation and when they
555                                // end it which is not ideal. Would prefer to only update
556                                // the revision once after a relation has been created.
557                                // perhaps there is a way to do this by inspecting the
558                                // ActorGraphModel?
559                                // ActorGraphModel agm = (ActorGraphModel) source;
560                                // agm.getLinkModel().tellMeIfThisIsTheBeginningOrEndOfARelationCreation()
561                                // For Now...
562
563                                // Set the id on the object directly
564                                KeplerLSID lsid = getId();
565                                if (isDebugging)
566                                        log.debug(lsid.toString());
567                                LSIDGenerator gen = LSIDGenerator.getInstance();
568                                KeplerLSID newLSID = gen.updateLsidRevision(lsid);
569                                setExpression(newLSID);
570                                if (isDebugging)
571                                        log.debug(newLSID.toString());
572
573                                // NOTE
574                                // Would prefer to update the revision by using a ChangeRequest
575                                // as is done in the updateRevision() method but this fouls up
576                                // the LinkModel for some reason and causes the linking to fail
577                                // completely
578                                // so the kludge fix for now is to not use a ChangeRequest to
579                                // update the lsid
580                                // updateRevision();
581                        }
582                } else if (source instanceof RenameConfigurer) {
583                        RenameConfigurer rc = (RenameConfigurer) source;
584                        NamedObj sourceObj = rc.getObject();
585                        if (isDebugging) 
586                                log.debug("RenameConfigurer object is " + sourceObj.getName() );
587                        if (container == sourceObj) {
588                                updateRevision();
589                        }
590                } else {
591                        if (isDebugging)
592                                log.debug("Handle MoMLChangeRequest from "
593                                                + source.getClass().getName());
594
595                        if (context == container) {
596                                updateRevision();
597                                
598                        } else {
599                                if (isDebugging)
600                                        log.debug("Ignore: MoMLChangeRequest outside of context");
601                        }
602                }
603
604        }
605
606        /**
607         * @param change
608         * @throws Exception
609         */
610        private void handleUndoChangeRequest(UndoChangeRequest change)
611                        throws Exception {
612
613                NamedObj context = change.getContext();
614                NamedObj container = this.getContainer();
615
616                if (isDebugging) {
617                        if (context != null) {
618                                log.debug("UndoChangeRequest.context " + context.getName()
619                                                + " " + context.getClass().getName());
620                        } else {
621                                log.debug("UndoChangeRequest.context is null");
622                        }
623                        log.debug("context: " + context);
624                        log.debug("container: " + container);
625                }
626
627                if (context == container) {
628                        updateRevision();
629
630                } else {
631                        if (isDebugging)
632                                log.debug("Ignore: UndoChangeRequest outside of context");
633                }
634
635        }
636
637        /**
638         * @param change
639         * @throws Exception
640         */
641        private void handleRedoChangeRequest(RedoChangeRequest change)
642                        throws Exception {
643
644                NamedObj context = change.getContext();
645                NamedObj container = this.getContainer();
646
647                if (isDebugging) {
648                        if (context != null) {
649                                log.debug("RedoChangeRequest.context " + context.getName()
650                                                + " " + context.getClass().getName());
651                        } else {
652                                log.debug("RedoChangeRequest.context is null");
653                        }
654                        log.debug("context: " + context);
655                        log.debug("container: " + container);
656                }
657
658                if (context == container) {
659                        updateRevision();
660
661                } else {
662                        if (isDebugging)
663                                log.debug("Ignore: RedoChangeRequest outside of context");
664                }
665        }
666
667        /**
668         * Check the given NamedObj to see if it matches the search ID.
669         * 
670         * @param no
671         * @return true if the given NamedObj has an attached NamedObjId that
672         *         matches the KeplerLSID that we are searching for
673         * @throws Exception
674         */
675        public static boolean idMatches(KeplerLSID lsid, NamedObj no,
676                        boolean matchRevision) {
677                if (isDebugging)
678                        log.debug("idMatches(" + no.getName() + ") "
679                                        + no.getClass().getName());
680
681                NamedObjId theID = NamedObjId.getIdAttributeFor(no);
682                if (theID != null) {
683                        if (matchRevision) {
684                                if (theID.getId().equals(lsid)) {
685                                        return true;
686                                }
687                        } else {
688                                if (theID.getId().equalsWithoutRevision(lsid)) {
689                                        return true;
690                                }
691                        }
692                }
693                return false;
694        }
695        
696        /*
697         * (non-Javadoc)
698         * 
699         * @see
700         * ptolemy.kernel.util.ChangeListener#changeExecuted(ptolemy.kernel.util
701         * .ChangeRequest)
702         */
703        @Override
704    public void changeExecuted(ChangeRequest change) {
705
706                if (isDebugging) {
707                        System.out.println();
708                        log.debug("********   NamedObjId.changeExecuted("
709                                        + change.getClass().getName() + ")");
710                        log.debug("ID: " + getId().toString());
711                        log.debug("change executed being run in " + this.getName() + " "
712                                        + this.getClass().getName());
713                        NamedObj c1 = this.getContainer();
714                        if (c1 != null) {
715                                log.debug("    with container " + c1.getName() + " "
716                                                + c1.getClass().getName());
717                                NamedObj c2 = c1.getContainer();
718                                if (c2 != null) {
719                                        log.debug("    which is inside of " + c2.getName() + " "
720                                                        + c2.getClass().getName());
721                                }
722                        }
723                        if (change.getSource() instanceof NamedObj) {
724                                log.debug("Source: "
725                                                + ((NamedObj) change.getSource()).getName() + " "
726                                                + change.getSource().getClass().getName());
727                        } else {
728                                log.debug("Source is not a named object but is a "
729                                                + change.getSource().getClass().getName());
730                        }
731                }
732
733                try {
734
735                        // The container of a NamedObjId should never be null
736                        NamedObj container = this.getContainer();
737                        if (container == null) {
738                                throw new Exception(
739                                                "NamedObjId.getContainer() is null on ChangeRequest");
740                        }
741                        if (change instanceof NamedObjIdChangeRequest) {
742                                handleNamedObjIdChangeRequest((NamedObjIdChangeRequest) change);
743
744                        } else if (change instanceof MoMLChangeRequest) {
745                                handleMoMLChangeRequest((MoMLChangeRequest) change);
746
747                        } else if (change instanceof UndoChangeRequest) {
748                                handleUndoChangeRequest((UndoChangeRequest) change);
749
750                        } else if (change instanceof RedoChangeRequest) {
751                                handleRedoChangeRequest((RedoChangeRequest) change);
752
753                        }
754
755                } catch (Exception e) {
756                        log.error("Failed to update revision: " + e.getMessage());
757                }
758        }
759
760        /**
761         * The updateRevision() method is used to retrieve the next available
762         * revision from the LSIDGenerator and updates the revision accordingly.
763         * 
764         * @throws Exception
765         */
766        public void updateRevision() throws Exception {
767                if (isDebugging)
768                        log.debug("updateRevision()");
769        
770                final NamedObj container = getContainer();
771                
772                // update the LSID
773                if (getId().isLocalToInstance()) {
774
775                        KeplerLSID lsidCheck = LSIDGenerator.getInstance()
776                                        .updateLsidRevision(getId());
777                        if (!lsidCheck.equals(getId())) {
778                                try {
779                    setExpression(lsidCheck.toString());
780                } catch (IllegalActionException e) {
781                    MessageHandler.error("Error setting LSID.", e);
782                    return;
783                }
784                        }
785
786                } else {
787                        
788                        NamedObjIdReferralList noirl;
789            try {
790                noirl = NamedObjId
791                                .getIDListAttributeFor(container);
792            } catch (Exception e) {
793                MessageHandler.error("Error get referral list.", e);
794                return;
795            }
796                        try {
797                                noirl.addReferral(getId());
798                        } catch (Exception e) {
799                            MessageHandler.error("Error adding referral.", e);
800                            return;
801                        }
802                        String updateReferralsMoml = "<property name=\""
803                                        + NamedObjIdReferralList.NAME + "\" class=\""
804                                        + noirl.getClass().getName() + "\" value=\""
805                                        + noirl.getExpression() + "\"/>";
806                        if (isDebugging)
807                                log.debug(updateReferralsMoml);
808                        NamedObjIdChangeRequest updateReferralRequest = new NamedObjIdChangeRequest(
809                                        this, getContainer(), updateReferralsMoml);
810                        getContainer().requestChange(updateReferralRequest);
811
812                        try {
813                                _id = LSIDGenerator.getInstance().getNewLSID();
814                        } catch (Exception e) {
815                            MessageHandler.error("Error getting new LSID.", e);
816                            return;
817                        }
818
819                }
820                
821                if (isDebugging)
822                        log.debug("The new revision: " + getId().toString());
823
824                requestNamedObjIdChange();
825                
826                for (int i = 0; i < valueListeners.size(); i++) {
827                        // notify any listeners of the change
828                        ValueListener listener = valueListeners
829                                        .elementAt(i);
830                        listener.valueChanged(this);
831                }
832
833
834        }
835
836        
837        public void requestNamedObjIdChange(){
838                
839                String updateEntityIdMoml = "<property name=\"" + NamedObjId.NAME
840                                + "\" class=\"" + getClass().getName() + "\" value=\""
841                                + getId() + "\"/>";
842                if (isDebugging)
843                        log.debug(updateEntityIdMoml);
844                NamedObjIdChangeRequest updateIdRequest = new NamedObjIdChangeRequest(
845                                this, getContainer(), updateEntityIdMoml);
846                getContainer().requestChange(updateIdRequest);
847                
848        }
849
850        /*
851         * (non-Javadoc)
852         * 
853         * @seeptolemy.kernel.util.ChangeListener#changeFailed(ptolemy.kernel.util.
854         * ChangeRequest, java.lang.Exception)
855         */
856        @Override
857    public void changeFailed(ChangeRequest change, Exception exception) {
858                if (isDebugging)
859                        log.debug("changeFailed()");
860
861        }
862
863        /** validate the expression. */
864        @Override
865    public Collection validate() {
866                // don't need to do anything here
867                return null;
868        }
869        
870        /**
871         * Override setContainer method to handle adding and removing the listener
872         * based on whether or not there is a container.
873         */
874        @Override
875    public void setContainer(NamedObj container) throws IllegalActionException,
876                NameDuplicationException {
877                if (container == null) {
878                        removeChangeListener(this);
879                } else {
880                        addChangeListener(this);
881                }
882                super.setContainer(container);
883        }
884        
885        /**
886         * Add a change listener, if the container is null the listener
887         * is not added.
888         */
889        @Override
890    public void addChangeListener(ChangeListener listener) {
891                NamedObj container = this.getContainer();
892                if (container != null) {
893                        super.addChangeListener(listener);
894                }
895        }
896
897        /**
898         * Description of the Method
899         * 
900         *@param obj
901         *            Description of the Parameter
902         *@return Description of the Return Value
903         */
904        @Override
905    public boolean equals(Object obj) {
906                if (!(obj instanceof NamedObjId)) {
907                        return false;
908                }
909                NamedObjId objId = (NamedObjId) obj;
910                String str = objId.getExpression();
911                if (this.getExpression() == null) {
912                        if (str != null) {
913                                return false;
914                        }
915                        return true;
916                }
917                return this.getExpression().equals(objId.getExpression());
918        }
919
920        /** Set if workflow changes should increment LSIDs. NOTE: this setting
921         *  only affects NamedObjId objects created after this method is called;
922         *  it does not change existing NamedObjIds.
923         */
924    public static void incrementLSIDOnWorkflowChange(boolean listen) {
925        _incrementLSIDOnWorkflowChange = listen;
926    }
927}