001/* A class that contains a number of decorated attributes. 002 003 Copyright (c) 2009-2014 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 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028 029package ptolemy.kernel.util; 030 031import java.io.IOException; 032import java.io.Writer; 033import java.util.List; 034 035import ptolemy.kernel.CompositeEntity; 036 037/////////////////////////////////////////////////////////////////// 038//// DecoratorAttributes 039 040/** 041A container for attributes created by a decorator. 042This is an attribute that will be contained by a target object that is 043decorated by a decorator. The parameters that the decorator creates 044will be contained by an instance of this object. 045These attributes can be retrieved by using 046{@link NamedObj#getDecoratorAttribute(Decorator,String)} or 047{@link NamedObj#getDecoratorAttributes(Decorator)}. 048 049@author Bert Rodiers 050@author Edward A. Lee 051@version $Id$ 052@since Ptolemy II 10.0 053@Pt.ProposedRating Yellow (eal) 054@Pt.AcceptedRating Red (rodiers) 055 */ 056public class DecoratorAttributes extends Attribute { 057 058 // FIXME: The decoratorName mechanism is very fragile. 059 // If the decorator changes name, the connection will be lost. 060 // If the container of the decorator changes name, it will again be lost. 061 // Probably the first constructor should not set the name, and this should 062 // be set only when MoML is to be exported. 063 // The second constructor should immediately look for the decorator by name. 064 // But it may not have been constructed yet. Hence, when a decorator is 065 // constructed, it should look for decorated objects and establish the link. 066 // This way, it doesn't matter which gets constructed first. 067 068 /** Construct a DecoratorAttributes instance to contain the 069 * decorator parameter for the specified container provided 070 * by the specified decorator. This constructor is used 071 * when retrieving decorator parameters from a target 072 * NamedObj that does not yet have the decorator parameters. 073 * @param container The target for the decorator. 074 * @param decorator The decorator. 075 * @exception IllegalActionException If the attribute is not of an 076 * acceptable class for the container, or if the name contains a period. 077 * @exception NameDuplicationException If the name coincides with 078 * an attribute already in the container. This should not occur. 079 */ 080 public DecoratorAttributes(NamedObj container, Decorator decorator) 081 throws IllegalActionException, NameDuplicationException { 082 super(container, container 083 .uniqueName("DecoratorAttributesFor_" + decorator.getName())); 084 _decorator = decorator; 085 086 decoratorName = new StringAttribute(this, "decoratorName"); 087 decoratorName.setVisibility(Settable.NONE); 088 } 089 090 /** Construct a DecoratorAttributes instance with the given name 091 * and container. This constructor is used when parsing MoML files, 092 * where it is assumed that the decorator is specified by name as 093 * the value of the {@link #decoratorName} parameter. 094 * @param container The container of this object. 095 * @param name The name of this attribute. 096 * @exception IllegalActionException If the attribute is not of an 097 * acceptable class for the container, or if the name contains a period. 098 * @exception NameDuplicationException If the name coincides with 099 * an attribute already in the container. 100 */ 101 public DecoratorAttributes(NamedObj container, String name) 102 throws IllegalActionException, NameDuplicationException { 103 super(container, name); 104 105 decoratorName = new StringAttribute(this, "decoratorName"); 106 decoratorName.setVisibility(Settable.NONE); 107 } 108 109 /////////////////////////////////////////////////////////////////// 110 //// parameters //// 111 112 /** The name of the decorator relative to the top-level of 113 * the model, to be stored in a MoML file 114 * to re-establish the connection with a decorator after saving 115 * and re-parsing the file. The name is relative to the top-level, 116 * rather than the full name, so that SaveAs works (where the name 117 * of the toplevel changes). 118 * FIXME: However, if you save a submodel, then this will not work! 119 * This is a string that is not visible to the user. 120 */ 121 public StringAttribute decoratorName; 122 123 /////////////////////////////////////////////////////////////////// 124 //// public methods //// 125 126 /** Override the base class to establish a link to the decorator 127 * if the argument is decoratorName. 128 * @exception IllegalActionException If the change is not acceptable 129 * to this container (not thrown in this base class). 130 */ 131 @Override 132 public void attributeChanged(Attribute attribute) 133 throws IllegalActionException { 134 if (attribute == decoratorName) { 135 // The decorator name is being set. 136 // Attempt to set the _decorator now, because its name 137 // or the name of one of its containers may change later. 138 // This will remain null if the decorator has not yet been 139 // constructed. 140 _decorator = getDecorator(); 141 } else { 142 super.attributeChanged(attribute); 143 } 144 } 145 146 /** Clone the object into the specified workspace. 147 * @param workspace The workspace for the cloned object. 148 * @exception CloneNotSupportedException Not thrown in this base class 149 * @return The new Attribute. 150 */ 151 @Override 152 public Object clone(Workspace workspace) throws CloneNotSupportedException { 153 DecoratorAttributes newObject = (DecoratorAttributes) super.clone( 154 workspace); 155 newObject._decorator = null; 156 return newObject; 157 } 158 159 /** Override the base class to first set the decoratorName attribute 160 * to the current name of the associated decorator, and then export 161 * using the superclass. 162 * 163 * @param output The output stream to write to. 164 * @param depth The depth in the hierarchy, to determine indenting. 165 * @param name The name of we use when exporting the description. 166 * @exception IOException If an I/O error occurs. 167 * @see #exportMoML(Writer, int) 168 */ 169 @Override 170 public void exportMoML(Writer output, int depth, String name) 171 throws IOException { 172 try { 173 _decorator = getDecorator(); 174 } catch (IllegalActionException e1) { 175 throw new IOException("Export failed.", e1); 176 } 177 if (_decorator == null) { 178 // No matching decorator is found. Discard the decorator attributes. 179 return; 180 } 181 // Record the name relative to the toplevel entity so that even if you 182 // do SaveAs (which changes the name of the toplevel) the decorator 183 // can still be found. 184 // FIXME: If you save a submodel, this will break the connection 185 // to the decorator. 186 try { 187 decoratorName.setExpression(_decorator.getName(toplevel())); 188 } catch (IllegalActionException e) { 189 throw new InternalErrorException(e); 190 } 191 192 // Now invoke the superclass to export MoML. 193 super.exportMoML(output, depth, name); 194 } 195 196 /** Return the decorator that is responsible for this DecoratorAttributes instance. 197 * @return The decorator, or null if there is none. 198 * @exception IllegalActionException If the decorator cannot be determined 199 * (e.g., a parameter cannot be evaluated). 200 */ 201 public Decorator getDecorator() throws IllegalActionException { 202 if (_decorator != null) { 203 // There is a decorator associated associated with this DecoratorAttributes. 204 // Check to see whether the decorator is still in scope. If it is not, 205 // then see whether there is a decorator in scope whose name matches, 206 // and establish a link with that one. This makes undo work. Specifically, 207 // if you delete a decorator, then undo, the undo will actually create a 208 // new decorator instance. This code re-establishes the connection. 209 NamedObj decoratorContainer = _decorator.getContainer(); 210 if (decoratorContainer == null 211 || !decoratorContainer.deepContains(this)) { 212 // Decorator is no longer in scope. Fall into code below to try to find it by name. 213 _decorator = null; 214 } 215 } 216 if (_decorator == null) { 217 // Retrieve the decorator using the decoratorName parameter. 218 String name = decoratorName.getExpression(); 219 if (name != null && !name.equals("")) { 220 // Find all the decorators in scope, and return the first one whose name matches. 221 NamedObj container = getContainer().getContainer(); 222 boolean crossedOpaqueBoundary = false; 223 while (container != null) { 224 List<Decorator> localDecorators = container 225 ._containedDecorators(); 226 for (Decorator decorator : localDecorators) { 227 if (!crossedOpaqueBoundary 228 || decorator.isGlobalDecorator()) { 229 if (decorator.getName(toplevel()).equals(name)) { 230 // We have a match. 231 _decorator = decorator; 232 return _decorator; 233 } 234 } 235 } 236 // FIXME: kernel.util should not have a dependence on kernel classes. 237 if (container instanceof CompositeEntity 238 && ((CompositeEntity) container).isOpaque()) { 239 crossedOpaqueBoundary = true; 240 } 241 container = container.getContainer(); 242 } 243 } 244 } 245 return _decorator; 246 } 247 248 /////////////////////////////////////////////////////////////////// 249 //// protected variables //// 250 251 /** The decorator.*/ 252 protected Decorator _decorator = null; 253}