001/* 002 * An attribute that contains a model described in MoML. 003 * 004 * Copyright (c) 2008-2014 The Regents of the University of California. All 005 * rights reserved. Permission is hereby granted, without written agreement and 006 * without 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 of 009 * this software. 010 * 011 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR 012 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT 013 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF 014 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 015 * 016 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 017 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 018 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN 019 * "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE 020 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 021 * 022 * PT_COPYRIGHT_VERSION_2 COPYRIGHTENDKEY 023 * 024 */ 025package ptolemy.moml; 026 027import java.io.IOException; 028import java.io.StringReader; 029import java.io.Writer; 030import java.net.MalformedURLException; 031import java.net.URI; 032import java.net.URL; 033 034import ptolemy.data.expr.FileParameter; 035import ptolemy.kernel.attributes.URIAttribute; 036import ptolemy.kernel.util.Attribute; 037import ptolemy.kernel.util.Configurable; 038import ptolemy.kernel.util.IllegalActionException; 039import ptolemy.kernel.util.NameDuplicationException; 040import ptolemy.kernel.util.NamedObj; 041import ptolemy.kernel.util.Workspace; 042import ptolemy.util.FileUtilities; 043 044/////////////////////////////////////////////////////////////////// 045//// MoMLModelAttribute 046 047/** 048 * An attribute that has a model described in MoML. 049 * The MoML is specified by calling {@link #configure(URL, String, String)}, 050 * or by including the MoML within <configure> tags in a MoML file. 051 * The MoML is returned by the {@link #getConfigureText()} method. 052 * The {@link #getContainedModel()} method returns the model specified 053 * by the MoML. 054 * <p> 055 * When an instance of this attribute is exported to MoML, the MoML 056 * description above will be included in the exported MoML within 057 * <configure> tags. 058 * <p> 059 * An instance of this attribute may wish to override the default 060 * "Look Inside" behavior by including an instance of 061 * ptolemy.vergil.toolbox.MoMLModelAttributeControllerFactory 062 * as an attribute contained by this instance. Instead of 063 * having an explicit compile-time dependency between this class and 064 * MoMLModelAttributeControllerFactory, derived classes should use MoML 065 * to set up the containment relationship. For example, 066 * <pre> 067 * <property name="MyAttribute" class="ptolemy.moml.MoMLModelAttribute"> 068 * <property name="_controllerFactory" class="ptolemy.vergil.toolbox.MoMLModelAttributeControllerFactory"> 069 * </property> 070 * <configure> 071 * ... my MoML text here ... 072 * </configure> 073 * </property> 074 * </pre> 075 * 076 * @author Dai Bui, Edward Lee, Ben Lickly, Charles Shelton 077 * @version $Id$ 078 * @since Ptolemy II 8.0 079 * @Pt.ProposedRating Red (tfeng) 080 * @Pt.AcceptedRating Red (tfeng) 081 */ 082public class MoMLModelAttribute extends Attribute implements Configurable { 083 084 /** Create a model attribute with the specified container and name. 085 * @param container The specified container. 086 * @param name The specified name. 087 * @exception IllegalActionException If the attribute is not of an 088 * acceptable class for the container, or if the name contains a period. 089 * @exception NameDuplicationException If the name coincides with an 090 * attribute already in the container. 091 */ 092 public MoMLModelAttribute(NamedObj container, String name) 093 throws NameDuplicationException, IllegalActionException { 094 super(container, name); 095 096 modelURL = new FileParameter(this, "modelURL"); 097 } 098 099 /////////////////////////////////////////////////////////////////// 100 //// parameters //// 101 102 /** URL from which to get the model. If this is specified, 103 * then the URL will be referenced in the exported configure tag 104 * rather than including the MoML for the model in the configure 105 * tag. This parameter is a string that defaults to empty. This string 106 * can either be an absolute, fully-qualified URL, or a URL relative 107 * to the container model's file location. A URL relative to the 108 * system's classpath can also be specified by a string starting with 109 * <code>$CLASSPATH/{relative URL}</code>. 110 */ 111 public FileParameter modelURL; 112 113 /////////////////////////////////////////////////////////////////// 114 //// public methods //// 115 116 /** React to a change in an attribute. If the modelURL attribute 117 * changes, reconfigure the MoML model with the new URL string. 118 * @param attribute The attribute that changed. 119 * @exception IllegalActionException Thrown if the URL string contained in the 120 * modelURL attribute is not valid. 121 */ 122 @Override 123 public void attributeChanged(Attribute attribute) 124 throws IllegalActionException { 125 126 if (attribute.equals(modelURL)) { 127 if (modelURL != null && !modelURL.stringValue().equals("")) { 128 String modelURLString = modelURL.stringValue(); 129 130 // The modelURLString could be either an absolute URL string 131 // or a file location relative to the container model file location. 132 // If it is the latter, we need to create an absolute URL string. 133 modelURLString = _createAbsoluteModelURLString(modelURLString); 134 135 try { 136 configure(null, modelURLString, null); 137 } catch (Exception ex) { 138 throw new IllegalActionException(this, ex, 139 "Could not " 140 + "configure the model contents of the " 141 + "MoMLModelAttribute with the given URL."); 142 } 143 } 144 } else { 145 super.attributeChanged(attribute); 146 } 147 } 148 149 /** Return a clone of this model attribute. This also creates a clone for the 150 * contained model. 151 * @param workspace The workspace for the cloned object. 152 * @return A clone. 153 * @exception CloneNotSupportedException Thrown if an error occurs while 154 * cloning the attribute or the contained model. 155 */ 156 @Override 157 public Object clone(Workspace workspace) throws CloneNotSupportedException { 158 MoMLModelAttribute newObject = (MoMLModelAttribute) super.clone( 159 workspace); 160 if (_model != null) { 161 newObject._model = (NamedObj) _model.clone(workspace()); 162 } 163 return newObject; 164 } 165 166 /** Construct and configure the contained model with the specified source and 167 * text. This parses the specified MoML text. 168 * @param base The base URL for relative references, or null if not known. 169 * @param source The URI of a document providing source, which if specified, 170 * will be used to obtain the text. In that case, the text argument will be 171 * ignored. 172 * @param text The MoML description. 173 * @exception Exception If the parsing fails. 174 */ 175 @Override 176 public void configure(URL base, String source, String text) 177 throws Exception { 178 _source = null; 179 MoMLParser parser = new MoMLParser(workspace()); 180 181 if (source != null && !source.trim().equals("")) { 182 _source = source; 183 _model = parser.parse(base, new URL(source)); 184 } else if (text != null && !text.trim().equals("")) { 185 _model = parser.parse(base, null, new StringReader(text)); 186 } else { 187 // source and text are null. 188 _model = null; 189 } 190 } 191 192 /** Return the input source that was specified the last time the configure 193 * method was called. 194 * @return The string representation of the input URL, or null if the 195 * no source has been used to configure this object, or null if no 196 * external source need be used to configure this object. 197 */ 198 @Override 199 public String getConfigureSource() { 200 return _source; 201 } 202 203 /** Return the MoML description of the model, if there is one, and 204 * null otherwise. 205 * @return The text to include in a configure tag. 206 */ 207 @Override 208 public String getConfigureText() { 209 // If the source is not null, there is no need for configure text, 210 // so return null. Otherwise return the model MoML text. 211 if (_source != null) { 212 return null; 213 } else if (_model != null) { 214 return _model.exportMoML(); 215 } 216 217 return null; 218 } 219 220 /** Return the contained model. 221 * @return The contained model. 222 */ 223 public NamedObj getContainedModel() { 224 return _model; 225 } 226 227 /////////////////////////////////////////////////////////////////// 228 //// protected methods //// 229 230 /** Write a MoML description this object, which includes a 231 * MoML description of the contained model within the <configure> tag. 232 * If the source URL is specified, do not write the MoML description. 233 * @param output The output stream to write to. 234 * @param depth The depth in the hierarchy, to determine indenting. 235 * @exception IOException If an I/O error occurs. 236 */ 237 @Override 238 protected void _exportMoMLContents(Writer output, int depth) 239 throws IOException { 240 super._exportMoMLContents(output, depth); 241 if (_source == null && _model != null) { 242 output.write(_getIndentPrefix(depth) + "<configure>\n"); 243 _model.exportMoML(output, depth + 1); 244 output.write(_getIndentPrefix(depth) + "</configure>\n"); 245 } 246 } 247 248 /////////////////////////////////////////////////////////////////// 249 //// protected variables //// 250 251 /** The contained model. This is protected so that derived classes 252 * can provide a default model. 253 */ 254 protected NamedObj _model; 255 256 /////////////////////////////////////////////////////////////////// 257 //// private methods //// 258 259 /** Return a string representing the full absolute URL of the contained model. 260 * If the input string is already an absolute URL, just return it. 261 * If the input string is a file path location relative to the model's 262 * directory, then return the full URL string by constructing the full 263 * URL from the base model path and the given relative path. 264 * 265 * @param modelURLString The model URL string specified by the modelURL parameter. 266 * @return A string representing the full absolute URL for the model URL. 267 * @exception IllegalActionException Thrown if the model URL string is invalid. 268 */ 269 private String _createAbsoluteModelURLString(String modelURLString) 270 throws IllegalActionException { 271 try { 272 // If the given string is a correctly formed URL, this constructor 273 // will not throw an exception. 274 new URL(modelURLString); 275 return modelURLString; 276 } catch (MalformedURLException e) { 277 try { 278 URI baseModelURI = URIAttribute.getModelURI(this); 279 URL modelURLObject = FileUtilities.nameToURL(modelURLString, 280 baseModelURI, null); 281 return modelURLObject.toString(); 282 } catch (IOException ioe) { 283 throw new IllegalActionException(this, ioe, 284 "Invalid MoMLModelAttribute model URL: " 285 + modelURLString); 286 } 287 } 288 } 289 290 /////////////////////////////////////////////////////////////////// 291 //// private variables //// 292 293 /** The source specified by the last call to configure(). */ 294 private String _source; 295}