[kepler-dev] CloneNotSupportedException

Christopher Brooks cxh at eecs.berkeley.edu
Wed Apr 20 14:41:11 PDT 2005


Edward modified ptolemy/kernel/Entity.java in the cvs tree to remove
the name restriction.  I've included the changed
Entity.clone(Workspace) method below.

We did not fix the code generator though.

The clone tests passed, and vergil starts up.  

Ilkay wrote:
> We were setting the container of the port to null to hide it.
>
> Is there a better way of doing this?

This can be done from the UI by selecting configure ports and
then clicking the "Hide" checkbox
The XML that is generated is:
   <property name="_hide" class="ptolemy.data.expr.SingletonParameter"
	        value="true">
   </property>

So, you could add a similar attribute with the name _hide and 
the value true using Java methods

_Christopher


/* An Entity is an aggregation of ports.

Copyright (c) 1997-2005 The Regents of the University of California.
All rights reserved.
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the above
copyright notice and the following two paragraphs appear in all copies
of this software.

IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
ENHANCEMENTS, OR MODIFICATIONS.

PT_COPYRIGHT_VERSION_2
COPYRIGHTENDKEY

*/
package ptolemy.kernel;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import ptolemy.kernel.util.Attribute;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.InternalErrorException;
import ptolemy.kernel.util.InvalidStateException;
import ptolemy.kernel.util.KernelException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.NamedList;
import ptolemy.kernel.util.NamedObj;
import ptolemy.kernel.util.Settable;
import ptolemy.kernel.util.Workspace;


//////////////////////////////////////////////////////////////////////////
//// Entity

/**
   An Entity is a vertex in a generalized graph. It is an aggregation
   of ports. The ports can be linked to relations. The
   relations thus represent connections between ports, and hence,
   connections between entities. To add a port to an entity, simply
   set its container to the entity.  To remove it, set its container
   to null, or to some other entity.
   <p>
   Entities are intended for flat graphs. Derived classes support
   hierarchy (clustered graphs) by defining entities that aggregate
   other entities.
   <p>
   An Entity can contain any instance of Port.  Derived classes may
   wish to constrain to a subclass of Port.  To do this, subclasses
   should override the public method newPort() to create a port of
   the appropriate subclass, and the protected method _addPort() to throw
   an exception if its argument is a port that is not of the appropriate
   subclass.
   <p>
   An Entity is created within a workspace.  If the workspace is
   not specified as a constructor argument, then the default workspace
   is used. The workspace is used to synchronize simultaneous accesses
   to a topology from multiple threads.  The workspace is immutable
   (it cannot be changed during the lifetime of the Entity).

   @author John S. Davis II, Edward A. Lee
   @version $Id: Entity.java,v 1.107 2005/04/20 21:22:48 eal Exp $
   @since Ptolemy II 0.2
   @Pt.ProposedRating Green (eal)
   @Pt.AcceptedRating Green (johnr)
   @see ptolemy.kernel.Port
   @see ptolemy.kernel.Relation
*/
public class Entity extends InstantiableNamedObj {
    /** Construct an entity in the default workspace with an empty string
     *  as its name.
     *  The object is added to the workspace directory.
     *  Increment the version number of the workspace.
     */
    public Entity() {
        super();
        _portList = new NamedList(this);
    }

    /** Construct an entity in the default workspace with the given name.
     *  If the name argument
     *  is null, then the name is set to the empty string.
     *  The object is added to the workspace directory.
     *  Increment the version number of the workspace.
     *  @param name The name of this object.
     *  @exception IllegalActionException If the name has a period.
     */
    public Entity(String name) throws IllegalActionException {
        super(name);
        _portList = new NamedList(this);
    }

    /** Construct an entity in the given workspace with an empty string
     *  as a name.
     *  If the workspace argument is null, use the default workspace.
     *  The object is added to the workspace directory.
     *  Increment the version of the workspace.
     *  @param workspace The workspace for synchronization and version tracking.
     */
    public Entity(Workspace workspace) {
        super(workspace);
        _portList = new NamedList(this);
    }

    /** Construct an entity in the given workspace with the given name.
     *  If the workspace argument is null, use the default workspace.
     *  If the name argument
     *  is null, then the name is set to the empty string.
     *  The object is added to the workspace directory.
     *  Increment the version of the workspace.
     *  @param workspace The workspace for synchronization and version tracking.
     *  @param name The name of this object.
     *  @exception IllegalActionException If the name has a period.
     */
    public Entity(Workspace workspace, String name)
            throws IllegalActionException {
        super(workspace, name);
        _portList = new NamedList(this);
    }

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    /** Clone the object into the specified workspace. The new object is
     *  <i>not</i> added to the directory of that workspace (you must do this
     *  yourself if you want it there).
     *  The result is a new entity with clones of the ports of the original
     *  entity.  The ports are set to the ports of the new entity.
     *  This method gets read access on the workspace associated with
     *  this object.
     *  @param workspace The workspace for the cloned object.
     *  @exception CloneNotSupportedException If cloned ports cannot have
     *   as their container the cloned entity (this should not occur), or
     *   if one of the attributes cannot be cloned.
     *  @return The new Entity.
     */
    public Object clone(Workspace workspace) throws CloneNotSupportedException {
        try {
            workspace().getReadAccess();

            Entity newEntity = (Entity) super.clone(workspace);
            newEntity._portList = new NamedList(newEntity);

            // Clone the ports.
            Iterator ports = portList().iterator();

            while (ports.hasNext()) {
                Port port = (Port) ports.next();
                Port newPort = (Port) port.clone(workspace);

                // Assume that since we are dealing with clones,
                // exceptions won't occur normally (the original was successfully
                // incorporated, so this one should be too).  If they do, throw an
                // InvalidStateException.
                try {
                    newPort.setContainer(newEntity);
                } catch (KernelException ex) {
                    workspace.remove(newEntity);
                    throw new InvalidStateException(this,
                            "Failed to clone an Entity: " + ex.getMessage());
                }
            }

            Class myClass = getClass();
            Field[] fields = myClass.getFields();

            for (int i = 0; i < fields.length; i++) {
                try {
                    if (fields[i].get(newEntity) instanceof Port) {
                        // Get the field name.
                        String fieldName = fields[i].getName();
                        // Get the port name. Note that by convention,
                        // this is the same as the field name. But it might
                        // not be.
                        String portName = ((Port)fields[i].get(this)).getName();
                        Port port = newEntity.getPort(portName);

                        if (port == null) {
                            throw new IllegalActionException(this,
                                    "Could not find a port named '" + portName + "';");
                        }

                        fields[i].set(newEntity, port);
                    }
                } catch (Exception ex) {
                    // CloneNotSupportedException does not have a
                    // constructor that takes a cause argument, so we call
                    // initCause() and then throw.
                    CloneNotSupportedException cloneException = new CloneNotSupportedException(
                            "Problem cloning '" + fields[i].getName() + "'");
                    cloneException.initCause(ex);
                    throw cloneException;
                }
            }

            _cloneFixAttributeFields(newEntity);
            return newEntity;
        } finally {
            workspace().doneReading();
        }
    }

--------

    Hi Chad,
    
    Ptolemy hackers has a limit of 40k for messages, and your last message
    was over that limit (sorry).  I truncated your message in my response
    below. 
    
    Chad writes:
    
    > Hey Christopher,
    > 
    > I forgot to address the problem of the actors with ports that actually 
    > do follow the naming convention.  I've attached a class that is an 
    > example of this.  Here's the port definition:
    > 
    > startTrigger = new TypedIOPort(this, "startTrigger", true, false);
    > 
    > and here's the stack trace:
    > 
    > [java] java.lang.CloneNotSupportedException: Problem cloning 
    > 'startTrigger'     [java]     at 
    > ptolemy.kernel.Entity.clone(Entity.java:224)
    >       [java]     at 
    > ptolemy.kernel.ComponentEntity.clone(ComponentEntity.java:125)
    >       [java]     at ptolemy.actor.AtomicActor.clone(AtomicActor.java:121)
    >       [java]     at 
    
    
    The problem is in the constructor, where you create startTrigger:
    
         startTrigger = new TypedIOPort(this, "startTrigger", true, false);
         new Attribute(startTrigger, "_showName");
         startTrigger.setContainer(null);
    
    If I remove the setContainer(null) line, then startTrigger is properly
    cloned.  I think setting setContainer(null) on a port basically removes
    it from the hierarchy, which causes no end of problems for clone,
    since then startTrigger is not found.  The error message is wrong
    though.
    
    I created a test for this in kernel/Entity.tcl.
    I'll see if I can come up with a better error message.
    
    We have group lunch today, we'll talk over the clone issue and
    get back to you.
    
    > The only test I have for showing this error is to start up kepler and 
    > open a new graph editor and watch the errors scroll by :)
    
    ptolemy/configs/test/allConfigs.tcl has tests that clone all the
    actors.
    
    
    > chad
    > 
    
    _Christopher
    
    > Christopher Brooks wrote:
    > > Hi Chad,
    > > The naming convention for ports is that the name of the port
    > > is the name of the java field.
    > > 
    > > There are a couple of reasons for this:
    > > 1) It means that many actors do not need their own clone(Workspace) met
   hod.
    > > We found that most developers had a hard time properly implementing
    > > the clone(Workspace) method so we modified Entity.clone(Workspace) 
    > > so that it uses reflection to properly clone the ports.
    > > 
    > > Entity has had this sort of cloning since at least 2001.
    > > 
    > > What did change was that in October, 2004, I modified 
    > > Entity.clone(Workspace) so that it handles misnamed ports in a 
    > > cleaner fashion.  That should have triggered new Exceptions
    > > being thrown though.
    > > 
    > > BTW - $PTII/ptolemy/configs/test/allConfigs.tcl includes tests that
    > > check port names and check cloning to ensure that the proper type
    > > constraints are cloned when necessary.  These tests run
    > > on the configuration.
    > > 
    > > 
    > > 2) I believe that the copernicus code generator might require
    > > that the names of ports and parameters should match the names
    > > of the variables.  See section 7.2 of Volume 2 of the Ptolemy Design
    > > doc:
    > > 
    > >   In order for existing actor code to be leveraged by the code
    > >   generator, it assumes that the code is written according to the
    > >   Ptolemy style for writing actors. This style assumes naming
    > >   conventions for the public fields of an actor class that refer to
    > >   parameters and ports of the actor. The code generator also assumes
    > >   that the ports and parameters of an actor are created in the class
    > >   constructor and not modified later. Some actors do not fit these
    > >   constraints and cannot be used directly in the code generator.  Such
    > >   actor classes cannot be used directly by the code generator,
    > >   although in some cases we have been able to have the code generator
    > >   deal specially with such actors. In other cases, the actor class
    > >   fits the constraints but cannot be effectively specialized using
    > >   generic techniques. Such actors can also be dealt with specially by
    > >   the code generator to more effectively generate code.
    > > 
    > > 3) I prefer strong naming conventions because I'm constantly looking
    > > at other people's code and I don't like stumbling for the mapping
    > > between the "a nice descriptive port name" and the name of the
    > > variable.  
    > > 
    > > 
    > > It sounds like there is a bug if the name of the Port that is set in
    > > the constructor is the same as the name of the field and the exception
    > > is being thrown.  
    > > 
    > > Do you have a test case I can take a look at?  Perhaps the port
    > > is not public?
    > > 
    > > One issue is that you probably have lots of actors with "a nice
    > > descriptive port name" as names.  Are these ports of these actors
    > > properly being cloned?  Maybe you should run the tests in
    > > ptolemy/configs/tests/allConfigs.tcl on you configuration and see what
    > > comes up.
    > > 
    > > It would be nice to provide some sort of compatibility functionality
    > > that would support your "a nice descriptive port name" ports and avoid
    > > the exception.  We could check for a property or something here.
    > > 
    > > Let me know what you think.
    > > 
    > > _Christopher
    > > 
    > > 
    > > --------
    > > 
    > >     Hi,
    > >     
    > >     Lately, we've been compiling kepler with the CVS version of PTII in
   stead 
    > >     of release 4.0.1.  We've started seeing errors (see bottom of email
   ) 
    > >     with a lot of our actors saying that a port cannot be cloned becaus
   e the 
    > >     variable name does not match the port name passed to the constructo
   r. 
    > >     I'm wondering why this suddenly started happening and why this shou
   ld be 
    > >     illegal behavior to begin with.  It seems reasonable to me to have 
   a 
    > >     declaration like
    > >     
    > >     TypedIOPort inputPort = new TypedIOPort(this, "a nice descriptive p
   ort 
    > >     name", true, false);
    > >     
    > >     The error message seems to strictly forbid this.  On top of this, 
    > >     sometimes the variable name *does* match the constructor name and i
   t 
    > >     still throws this exception.
    > >     
    > >     My questions are: Does anyone know why this suddenly started happen
   ing 
    > >     and why is this a problem in the first place?  Is there anyway to f
   ix 
    > >     this, short of changing the variable names in all of our actors?
    > >     
    > >     thanks,
    > >     chad
    > >     
    > >     java.lang.CloneNotSupportedException: Problem cloning 'outputLog'
    > >           [java]     at ptolemy.kernel.Entity.clone(Entity.java:224)
    > >           [java]     at 
    > >     ptolemy.kernel.ComponentEntity.clone(ComponentEntity.java:125)
    > >           [java]     at ptolemy.actor.AtomicActor.clone(AtomicActor.jav
   a:121)
    > >           [java]     at 
    > >     org.ecoinformatics.seek.sms.AnnotationEngine.buildTreeModel(Annotat
   ionEng


More information about the Kepler-dev mailing list