001/** Default preferences definition for Vergil. */
002
003/*
004 Copyright (c) 2006-2014 The Regents of the University of California.
005 All rights reserved.
006
007 Permission is hereby granted, without written agreement and without
008 license or royalty fees, to use, copy, modify, and distribute this
009 software and its documentation for any purpose, provided that the above
010 copyright notice and the following two paragraphs appear in all copies
011 of this software.
012
013 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
014 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
015 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
016 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
017 SUCH DAMAGE.
018
019 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
020 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
021 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
022 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
023 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
024 ENHANCEMENTS, OR MODIFICATIONS.
025 */
026
027package ptolemy.actor.gui;
028
029import java.io.File;
030import java.io.FileWriter;
031import java.io.IOException;
032import java.net.MalformedURLException;
033import java.net.URL;
034import java.util.Iterator;
035
036import ptolemy.data.BooleanToken;
037import ptolemy.data.Token;
038import ptolemy.data.expr.Constants;
039import ptolemy.data.expr.ModelScope;
040import ptolemy.data.expr.Parameter;
041import ptolemy.data.expr.ScopeExtendingAttribute;
042import ptolemy.data.expr.SingletonParameter;
043import ptolemy.data.expr.StringParameter;
044import ptolemy.data.expr.Variable;
045import ptolemy.data.type.BaseType;
046import ptolemy.kernel.util.Attribute;
047import ptolemy.kernel.util.IllegalActionException;
048import ptolemy.kernel.util.NameDuplicationException;
049import ptolemy.kernel.util.NamedObj;
050import ptolemy.kernel.util.Settable;
051import ptolemy.moml.MoMLParser;
052import ptolemy.util.StringUtilities;
053
054///////////////////////////////////////////////////////////////////
055//// PtolemyPreferences
056
057/**
058 * Default preferences definition for Vergil. This is defined as a class rather
059 * than in MoML so that the inheritance mechanism prevents exported MoML for
060 * every model from duplicating this information.
061 *
062 * @author Edward A. Lee, Contributor: Bert Rodiers
063 * @version $Id$
064 @since Ptolemy II 5.2
065 * @Pt.ProposedRating Yellow (eal)
066 * @Pt.AcceptedRating Red (cxh)
067 */
068public class PtolemyPreferences extends ScopeExtendingAttribute {
069    /** Construct an instance of the preferences attribute.
070     *  @param container The container.
071     *  @param name The name.
072     *  @exception IllegalActionException If the attribute is not of an
073     *   acceptable class for the container, or if the name contains a period.
074     *  @exception NameDuplicationException If the name coincides with
075     *   an attribute already in the container.
076     */
077    public PtolemyPreferences(NamedObj container, String name)
078            throws IllegalActionException, NameDuplicationException {
079        super(container, name);
080
081        // Give the default values for all the preferences.
082        // Names start with underscores by convention to minimize
083        // the probability of conflict with model-specific parameters.
084        // Note that the default values here should conform with the
085        // defaults used in the code where these preferences are checked,
086        // if there are any.  It's a good idea to provide them in
087        // case setAsDefault() is never called.
088        Parameter relationSize = new Parameter(this, "_relationSize");
089        relationSize.setTypeEquals(BaseType.DOUBLE);
090        relationSize.setExpression("12.0");
091        relationSize.setDisplayName("Relation size");
092
093        Parameter portSize = new Parameter(this, "_portSize");
094        portSize.setTypeEquals(BaseType.DOUBLE);
095        portSize.setExpression("4.0");
096        portSize.setDisplayName("Port size");
097
098        Parameter linkBendRadius = new Parameter(this, "_linkBendRadius");
099        linkBendRadius.setTypeEquals(BaseType.DOUBLE);
100        linkBendRadius.setExpression("20.0");
101        linkBendRadius.setDisplayName("Link bend radius");
102
103        backgroundColor = new ColorAttribute(this, "backgroundColor");
104        backgroundColor.setExpression("{0.9, 0.9, 0.9, 1.0}");
105        backgroundColor.setDisplayName("Background Color");
106
107        StringParameter showParameters = new StringParameter(this,
108                "_showParameters");
109        showParameters.addChoice("None");
110        showParameters.addChoice("Overridden parameters only");
111        showParameters.addChoice("All");
112        showParameters.setExpression("None");
113        showParameters.setDisplayName("Show parameters");
114
115        Parameter checkWidthConsistencyAtMultiports = new Parameter(this,
116                "_checkWidthConsistencyAtMultiports");
117        checkWidthConsistencyAtMultiports.setTypeEquals(BaseType.BOOLEAN);
118        checkWidthConsistencyAtMultiports.setExpression("true");
119        checkWidthConsistencyAtMultiports
120                .setDisplayName("Check width consistency at multiports");
121
122        Parameter checkWidthConstraints = new Parameter(this,
123                "_checkWidthConstraints");
124        checkWidthConstraints.setTypeEquals(BaseType.BOOLEAN);
125        checkWidthConstraints.setExpression("true");
126        checkWidthConstraints.setDisplayName("Check width constraints");
127
128        Parameter defaultInferredWidthTo1 = new Parameter(this,
129                "_defaultInferredWidthTo1");
130        defaultInferredWidthTo1.setTypeEquals(BaseType.BOOLEAN);
131        defaultInferredWidthTo1.setExpression("false");
132        defaultInferredWidthTo1.setDisplayName("Default inferred width to 1");
133
134        // The icon.
135        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-60\" y=\"-10\" "
136                + "width=\"120\" height=\"20\" " + "style=\"fill:#00FFFF\"/>\n"
137                + "<text x=\"-55\" y=\"5\" "
138                + "style=\"font-size:14; font-family:SansSerif; fill:blue\">\n"
139                + "LocalPreferences\n" + "</text>\n" + "</svg>\n");
140        // Hide the name.
141        SingletonParameter hideName = new SingletonParameter(this, "_hideName");
142        hideName.setToken(BooleanToken.TRUE);
143        hideName.setVisibility(Settable.EXPERT);
144    }
145
146    ///////////////////////////////////////////////////////////////////
147    ////                       public parameters                   ////
148
149    /** The background color. */
150    public ColorAttribute backgroundColor;
151
152    ///////////////////////////////////////////////////////////////////
153    ////                         public methods                    ////
154
155    /** Get the PtolemyPreferences within the specified configuration.
156     *  @param configuration  The configuration in which to search for
157     *  {@link #PREFERENCES_WITHIN_CONFIGURATION}
158     *  @return The associated PtolemyPreferences or null if not found.
159     *  @exception IllegalActionException If there is a problem getting the
160     *  {@link #PREFERENCES_WITHIN_CONFIGURATION} attribute.
161     */
162    public static PtolemyPreferences getPtolemyPreferencesWithinConfiguration(
163            Configuration configuration) throws IllegalActionException {
164        PtolemyPreferences preferences = null;
165
166        try {
167            preferences = (PtolemyPreferences) configuration.getAttribute(
168                    PtolemyPreferences.PREFERENCES_WITHIN_CONFIGURATION,
169                    PtolemyPreferences.class);
170        } catch (IllegalActionException ex) {
171            return null;
172        }
173        return preferences;
174    }
175
176    /** Check to see whether a preference of the specified name is
177     *  defined in the specified context, and if it is, return its value.
178     *  Note that if there is an error in the expression for the preference,
179     *  then this method will return null and report the error to standard out.
180     *  This is done because we assume the error will normally be caught
181     *  before this method is called.
182     *  @param context The context for the preference.
183     *  @param preferenceName The name of the preference.
184     *  @return The value of the preference, or null if it is not set.
185     */
186    public static Token preferenceValue(NamedObj context,
187            String preferenceName) {
188        return ModelScope.preferenceValue(context, preferenceName);
189    }
190
191    /** Check to see whether a preference of the specified name is
192     *  defined in the container of the specified context, either directly
193     *  or within an instance of PtolemyPreferences, or
194     *  globally, and if it is, return it's value. Do not look any higher
195     *  in the hierarchy.
196     *  Note that if there is an error in the expression for the preference,
197     *  then this method will return null and report the error to standard out.
198     *  This is done because we assume the error will normally be caught
199     *  before this method is called.
200     *  @param context The context for the preference.
201     *  @param preferenceName The name of the preference.
202     *  @return The value of the preference, or null if it is not set.
203     */
204    public static Token preferenceValueLocal(NamedObj context,
205            String preferenceName) {
206        try {
207            NamedObj container = context.getContainer();
208            if (container != null) {
209                Attribute attribute = container.getAttribute(preferenceName);
210                if (attribute instanceof Variable) {
211                    return ((Variable) attribute).getToken();
212                }
213                Iterator<?> preferences = container
214                        .attributeList(PtolemyPreferences.class).iterator();
215                while (preferences.hasNext()) {
216                    PtolemyPreferences preference = (PtolemyPreferences) preferences
217                            .next();
218                    attribute = preference.getAttribute(preferenceName);
219                    if (attribute instanceof Variable) {
220                        return ((Variable) attribute).getToken();
221                    }
222                }
223            }
224        } catch (IllegalActionException ex) {
225            System.out.println("Warning: Invalid preference: " + ex);
226        }
227        // If no scoped variable is found, try for a defined constant.
228        return Constants.get(preferenceName);
229    }
230
231    /** Save the preference values in this instance to the user
232     *  preferences file.
233     *  @exception IOException If an error occurs writing the file.
234     */
235    public void save() throws IOException {
236        String libraryName = StringUtilities.preferencesDirectory()
237                + PREFERENCES_FILE_NAME;
238        File file = new File(libraryName);
239        FileWriter writer = null;
240        try {
241            writer = new FileWriter(file);
242            exportMoML(writer);
243        } finally {
244            if (writer != null) {
245                try {
246                    writer.close();
247                } catch (IOException ex) {
248                    System.out.println(
249                            "Failed to close \"" + libraryName + "\": " + ex);
250
251                }
252            }
253        }
254    }
255
256    /** Set the values in this instance of PtolemyPreferences to be
257     *  the default values by creating entries in the Constants class
258     *  so that these values are accessible to any expression.
259     *  @exception IllegalActionException If any expression for
260     *   a preference cannot be evaluated.
261     */
262    public void setAsDefault() throws IllegalActionException {
263        // Make the current global variables conform with any
264        // overridden preference values.
265        Iterator<?> parameters = attributeList(Variable.class).iterator();
266
267        while (parameters.hasNext()) {
268            Variable parameter = (Variable) parameters.next();
269            Token token = parameter.getToken();
270            Constants.add(parameter.getName(), token);
271        }
272    }
273
274    /** Look for a default preferences object within the
275     *  specified configuration, and set it as the default
276     *  preferences. Then look for a user preferences file,
277     *  and override the default preferences with the contents
278     *  of that file. This method prints warning messages
279     *  on standard out if there are problems with the
280     *  preferences.
281     *  @param configuration The specified configuration.
282     */
283    public static void setDefaultPreferences(Configuration configuration) {
284        PtolemyPreferences preferences = null;
285        try {
286            preferences = getPtolemyPreferencesWithinConfiguration(
287                    configuration);
288        } catch (IllegalActionException ex) {
289            System.out.println("Warning: Problem with preferences attribute "
290                    + "in the configuration: " + ex.getMessage());
291
292            // Can't do anything further.
293            return;
294        }
295
296        if (preferences == null) {
297            // No preferences found in the configuration at
298            // location "actor library.Utilities.LocalPreferences"
299            return;
300        }
301
302        // Now override with the user file, if present.
303        String libraryName = null;
304
305        try {
306            libraryName = StringUtilities.preferencesDirectory()
307                    + PREFERENCES_FILE_NAME;
308        } catch (Exception ex) {
309            System.out.println("Warning: Failed to get the preferences "
310                    + "directory (-sandbox always causes this): "
311                    + ex.getMessage());
312
313            // Can't do anything further.
314            return;
315        }
316
317        File file = new File(libraryName);
318
319        if (file.isFile() && file.canRead()) {
320            // If we have a jar URL, convert spaces to %20
321            URL fileURL;
322
323            try {
324                fileURL = JNLPUtilities
325                        .canonicalizeJarURL(file.toURI().toURL());
326            } catch (MalformedURLException ex) {
327                // This should not occur.
328                System.err.println("Malformed preferences URL: " + ex);
329                return;
330            }
331
332            MoMLParser parser = new MoMLParser(preferences.workspace());
333            parser.setContext(preferences.getContainer());
334
335            // Set the ErrorHandler so that if we have
336            // compatibility problems between devel and production
337            // versions, we can skip that element.
338            // MoMLParser.setErrorHandler(new VergilErrorHandler());
339
340            try {
341                parser.parse(fileURL, fileURL);
342            } catch (Exception ex) {
343                System.out
344                        .println("Failed to read user preferences file: " + ex);
345            }
346        }
347
348        try {
349            preferences.setAsDefault();
350        } catch (IllegalActionException ex) {
351            System.out.println("Warning: Problem with preferences value: "
352                    + ex.getMessage());
353        }
354    }
355
356    ///////////////////////////////////////////////////////////////////
357    ////                         public variables                  ////
358
359    /** The file name where user-defined preferences are stored. */
360    public static final String PREFERENCES_FILE_NAME = "PtolemyPreferences.xml";
361
362    /** The location with the configuration of the preferences attribute. */
363    public static final String PREFERENCES_WITHIN_CONFIGURATION = "actor library.Utilities.LocalPreferences";
364}