001/* An attribute that has a string value.
002
003 Copyright (c) 2000-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 */
027package ptolemy.kernel.util;
028
029import java.io.IOException;
030import java.io.Writer;
031import java.util.Collection;
032import java.util.Iterator;
033import java.util.LinkedList;
034import java.util.List;
035
036import ptolemy.util.StringUtilities;
037
038///////////////////////////////////////////////////////////////////
039//// StringAttribute
040
041/**
042 An attribute that has a string value.
043 Use setExpression() to define the value, as in for example
044 <pre>
045 attribute.setExpression("xxx");
046 </pre>
047 <p>The default value of the string contained by this attribute is the empty
048 string.
049
050 <p>By default, an instance of this class is fully visible in
051 a user interface.  The visibility is indicated to the user
052 interface when the user interface calls the getVisibility() method
053 of this class and the value Settable.FULL is returned to the userInterface.
054
055 <p>Note that the string value within StringAttribute cannot reference
056 other StringAttributes or Parameters, so if an actor has a public
057 StringAttribute, then one cannot make the value of that attribute
058 dependent on a value higher in the hierarchy.  Usually, actors have
059 public ptolemy.data.expr.Parameters instead of public StringAttributes
060 so that the value can reference other parameters.  The primary reason
061 to use StringAttribute is if you want a string that will not be parsed
062 and you do not want to type a leading a trailing double quote.
063
064 @see Settable#FULL
065 @author Edward A. Lee
066 @version $Id$
067 @since Ptolemy II 1.0
068 @Pt.ProposedRating Green (eal)
069 @Pt.AcceptedRating Green (cxh)
070 */
071public class StringAttribute extends AbstractSettableAttribute {
072    /** Construct an attribute in the default workspace with an empty string
073     *  as its name.
074     *  The object is added to the directory of the workspace.
075     *  Increment the version number of the workspace.
076     */
077    public StringAttribute() {
078        super();
079        _value = "";
080    }
081
082    /** Construct an attribute in the specified workspace with an empty
083     *  string as a name.
084     *  The object is added to the directory of the workspace.
085     *  Increment the version number of the workspace.
086     *  @param workspace The workspace that will list the attribute.
087     */
088    public StringAttribute(Workspace workspace) {
089        super(workspace);
090        _value = "";
091    }
092
093    /** Construct an attribute with the given name contained by the specified
094     *  container. The container argument must not be null, or a
095     *  NullPointerException will be thrown.  This attribute will use the
096     *  workspace of the container for synchronization and version counts.
097     *  If the name argument is null, then the name is set to the empty
098     *  string. The object is added to the directory of the workspace
099     *  if the container is null.
100     *  Increment the version of the workspace.
101     *  @param container The container.
102     *  @param name The name of this attribute.
103     *  @exception IllegalActionException If the attribute is not of an
104     *   acceptable class for the container, or if the name contains a period.
105     *  @exception NameDuplicationException If the name coincides with
106     *   an attribute already in the container.
107     */
108    public StringAttribute(NamedObj container, String name)
109            throws IllegalActionException, NameDuplicationException {
110        super(container, name);
111        _value = "";
112    }
113
114    ///////////////////////////////////////////////////////////////////
115    ////                         public methods                    ////
116
117    /** Add a listener to be notified when the value of this attribute changes.
118     *  If the listener is already on the list of listeners, then do nothing.
119     *  @param listener The listener to add.
120     *  @see #removeValueListener(ValueListener)
121     */
122    @Override
123    public void addValueListener(ValueListener listener) {
124        if (_valueListeners == null) {
125            _valueListeners = new LinkedList();
126        }
127
128        if (!_valueListeners.contains(listener)) {
129            _valueListeners.add(listener);
130        }
131    }
132
133    /** Clone the object into the specified workspace.
134     *  @param workspace The workspace for the new object.
135     *  @return A new NamedObj.
136     *  @exception CloneNotSupportedException If any of the attributes
137     *   cannot be cloned.
138     */
139    @Override
140    public Object clone(Workspace workspace) throws CloneNotSupportedException {
141        StringAttribute newObject = (StringAttribute) super.clone(workspace);
142        newObject._valueListeners = null;
143        return newObject;
144    }
145
146    /** Write a MoML description of this object, unless it is non-persistent.
147     *  MoML is an XML modeling markup language.
148     *  In this class, the object is identified by the "property"
149     *  element, with "name", "class", and "value" (XML) attributes.
150     *  The body of the element, between the "&lt;property&gt;"
151     *  and "&lt;/property&gt;", is written using
152     *  the _exportMoMLContents() protected method, so that derived classes
153     *  can override that method alone to alter only how the contents
154     *  of this object are described.
155     *  The text that is written is indented according to the specified
156     *  depth, with each line (including the last one)
157     *  terminated with a newline.
158     *  @param output The output stream to write to.
159     *  @param depth The depth in the hierarchy, to determine indenting.
160     *  @param name The name to use instead of the current name.
161     *  @exception IOException If an I/O error occurs.
162     *  @see #isPersistent()
163     */
164    @Override
165    public void exportMoML(Writer output, int depth, String name)
166            throws IOException {
167        if (_isMoMLSuppressed(depth)) {
168            return;
169        }
170
171        String value = getExpression();
172        String valueTerm = "";
173
174        if (value != null && !value.equals("")) {
175            valueTerm = " value=\"" + StringUtilities.escapeForXML(value)
176                    + "\"";
177        }
178
179        // Escape any < character in name. unescapeForXML occurs in
180        // NamedObj.setName(String).
181        name = StringUtilities.escapeForXML(name);
182
183        output.write(_getIndentPrefix(depth) + "<" + _elementName + " name=\""
184                + name + "\" class=\"" + getClassName() + "\"" + valueTerm
185                + ">\n");
186        _exportMoMLContents(output, depth + 1);
187        output.write(_getIndentPrefix(depth) + "</" + _elementName + ">\n");
188    }
189
190    /** Get the value that has been set by setExpression(),
191     *  or null if there is none.
192     *  @return The string value.
193     *  @see #setExpression(String)
194     */
195    @Override
196    public String getExpression() {
197        return _value;
198    }
199
200    /** Get the visibility of this attribute, as set by setVisibility().
201     *  The visibility is set by default to FULL.
202     *  @return The visibility of this attribute.
203     *  @see #setVisibility(Settable.Visibility)
204     */
205    @Override
206    public Settable.Visibility getVisibility() {
207        return _visibility;
208    }
209
210    /** Remove a listener from the list of listeners that is
211     *  notified when the value of this attribute changes.  If no such listener
212     *  exists, do nothing.
213     *  @param listener The listener to remove.
214     *  @see #addValueListener(ValueListener)
215     */
216    @Override
217    public void removeValueListener(ValueListener listener) {
218        if (_valueListeners != null) {
219            _valueListeners.remove(listener);
220        }
221    }
222
223    /** Set the value of the string attribute and notify the container
224     *  of the value of this attribute by calling attributeChanged().
225     *  Notify any value listeners of this attribute.
226     *  @param expression The value of the string attribute.
227     *  @exception IllegalActionException If the change is not acceptable
228     *   to the container.
229     *  @see #getExpression()
230     */
231    @Override
232    public void setExpression(String expression) throws IllegalActionException {
233        super.setExpression(expression);
234        _value = expression;
235
236        // Notify the container and any value listeners immediately,
237        // rather than deferring to validate().
238        NamedObj container = getContainer();
239
240        if (container != null) {
241            container.attributeChanged(this);
242        }
243
244        if (_valueListeners != null) {
245            Iterator listeners = _valueListeners.iterator();
246
247            while (listeners.hasNext()) {
248                ValueListener listener = (ValueListener) listeners.next();
249                listener.valueChanged(this);
250            }
251        }
252    }
253
254    /** Set the visibility of this attribute.  The argument should be one
255     *  of the public static instances in Settable.
256     *  @param visibility The visibility of this attribute.
257     *  @see #getVisibility()
258     */
259    @Override
260    public void setVisibility(Settable.Visibility visibility) {
261        _visibility = visibility;
262    }
263
264    /** Do nothing.
265     *  There is no need to notify the container or listeners of this
266     *  attribute because they have presumably already been notified
267     *  in setExpression().
268     *  @return Null, indicating that no other instances of Settable are
269     *   validated.
270     *  @exception IllegalActionException If the change is not acceptable
271     *   to the container.
272     */
273    @Override
274    public Collection validate() throws IllegalActionException {
275        return null;
276    }
277
278    ///////////////////////////////////////////////////////////////////
279    ////                         protected methods                 ////
280
281    /** Propagate the value of this object to the
282     *  specified object. The specified object is required
283     *  to be an instance of the same class as this one, or
284     *  a ClassCastException will be thrown.
285     *  @param destination Object to which to propagate the
286     *   value.
287     *  @exception IllegalActionException If the value cannot
288     *   be propagated.
289     */
290    @Override
291    protected void _propagateValue(NamedObj destination)
292            throws IllegalActionException {
293        ((Settable) destination).setExpression(getExpression());
294    }
295
296    ///////////////////////////////////////////////////////////////////
297    ////                         private variables                 ////
298    // The value.
299    private String _value;
300
301    // Listeners for changes in value.
302    private List _valueListeners;
303
304    // The visibility of this attribute, which defaults to FULL.
305    private Settable.Visibility _visibility = Settable.FULL;
306}