001/* An interface used by the expression parser for identifier lookup. 002 003 Copyright (c) 2001-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.data.expr; 030 031import java.util.HashSet; 032import java.util.Iterator; 033import java.util.List; 034import java.util.Set; 035 036import ptolemy.data.Token; 037import ptolemy.kernel.ComponentEntity; 038import ptolemy.kernel.ComponentRelation; 039import ptolemy.kernel.CompositeEntity; 040import ptolemy.kernel.Entity; 041import ptolemy.kernel.Port; 042import ptolemy.kernel.Relation; 043import ptolemy.kernel.util.Attribute; 044import ptolemy.kernel.util.IllegalActionException; 045import ptolemy.kernel.util.LazyComposite; 046import ptolemy.kernel.util.NamedObj; 047import ptolemy.kernel.util.ScopeExtender; 048 049////////////////////////////////////////////////////////////////////////// 050//// ModelScope 051 052/** 053 An abstract class that is useful for implementing expression language 054 scopes for Ptolemy models. 055 056 <p>{@link #getScopedVariable(Variable, NamedObj, String)} is the 057 primary entry point, used by the Expression actor and other code 058 to look up Variables by name.</p> 059 060 @author Xiaojun Liu, Steve Neuendorffer, Contributor: Bert Rodiers 061 @version $Id$ 062 @since Ptolemy II 2.1 063 @Pt.ProposedRating Red (liuxj) 064 @Pt.AcceptedRating Red (liuxj) 065 @see ptolemy.data.expr.PtParser 066 */ 067public abstract class ModelScope implements ParserScope { 068 /** Return a list of object names in scope for variables in the 069 * given container. 070 * @param container The container of this scope. 071 * @return a list of object names in scope for variables in the 072 * given container. 073 */ 074 public static Set<String> getAllScopedObjectNames(NamedObj container) { 075 Set<String> identifiers = new HashSet<String>(); 076 identifiers.add("this"); 077 while (container != null) { 078 for (Object attribute : container.attributeList()) { 079 identifiers.add(((Attribute) attribute).getName()); 080 } 081 if (container instanceof Entity) { 082 for (Object port : ((Entity) container).portList()) { 083 identifiers.add(((Port) port).getName()); 084 } 085 } 086 if (container instanceof CompositeEntity) { 087 for (Object entity : ((CompositeEntity) container) 088 .entityList()) { 089 identifiers.add(((Entity) entity).getName()); 090 } 091 092 for (Object relation : ((CompositeEntity) container) 093 .relationList()) { 094 identifiers.add(((Relation) relation).getName()); 095 } 096 } 097 container = container.getContainer(); 098 } 099 100 return identifiers; 101 } 102 103 /** Return a list of variable names in scope for variables in the 104 * given container. Exclude the given variable from being 105 * considered in scope. 106 * @param exclude The variable to exclude from the scope. 107 * @param container The container of this scope. 108 * @return A list of variable names in scope for variables in the 109 * given container. 110 */ 111 public static Set<String> getAllScopedVariableNames(Variable exclude, 112 NamedObj container) { 113 List variableList = container.attributeList(Variable.class); 114 variableList.remove(exclude); 115 116 Set<String> nameSet = new HashSet<String>(); 117 118 for (Iterator variables = variableList.iterator(); variables 119 .hasNext();) { 120 Variable variable = (Variable) variables.next(); 121 nameSet.add(variable.getName()); 122 } 123 124 // Get variables higher in scope. Moving up the hierarchy 125 // terminates when the container is null. 126 NamedObj aboveContainer = container.getContainer(); 127 128 if (aboveContainer != null) { 129 nameSet.addAll(getAllScopedVariableNames(exclude, aboveContainer)); 130 } 131 132 // Get variables in scope extenders. Moving down the scope 133 // extenders terminates at hierarchy leaves. 134 Iterator extenders = container.attributeList(ScopeExtender.class) 135 .iterator(); 136 137 while (extenders.hasNext()) { 138 ScopeExtender extender = (ScopeExtender) extenders.next(); 139 // It would be nice if ScopeExtender and NamedObj were common in 140 // some way to avoid this cast. 141 142 // This change was necessary for Java Code Generation. 143 // We don't want to call getAllScopedVariableNames() here because 144 // we will end up in an endless loop. 145 146 // Test 3.1 in ptolemy/actor/parameters/test/ParameterSetModel.tcl 147 // will go in an endless loop if the next two lines are on commente: 148 //nameSet.addAll(getAllScopedVariableNames(exclude, 149 // (NamedObj) extender)); 150 151 // This is safer, but does it include everything? 152 for (Iterator attributes = extender.attributeList() 153 .iterator(); attributes.hasNext();) { 154 Attribute attribute = (Attribute) attributes.next(); 155 if (attribute instanceof Variable) { 156 nameSet.add(attribute.getName()); 157 } 158 } 159 } 160 return nameSet; 161 } 162 163 /** Get the attribute with the given name in the scope of the given 164 * container. If the name contains the "::" scoping specifier, 165 * then an attribute more deeply in the hierarchy is searched 166 * for. The scope of the object includes any container of the 167 * given object, and any attribute contained in a scope extending 168 * attribute inside any of those containers. 169 * @param exclude An attribute to exclude from the search. 170 * @param container The container to search upwards from. 171 * @param name The attribute name to search for. 172 * @return The attribute with the given name or null if the attribute 173 * does not exist. 174 */ 175 public static Attribute getScopedAttribute(Attribute exclude, 176 NamedObj container, String name) { 177 // getScopedAttribute() is used by the SetVariable actor. 178 String insideName = name.replaceAll("::", "."); 179 180 while (container != null) { 181 Attribute result = _searchAttributeIn(exclude, container, 182 insideName); 183 184 if (result != null) { 185 return result; 186 } else { 187 List attributes = container 188 .attributeList(ContainmentExtender.class); 189 Iterator attrIterator = attributes.iterator(); 190 NamedObj extendedContainer = null; 191 while (extendedContainer == null && attrIterator.hasNext()) { 192 ContainmentExtender extender = (ContainmentExtender) attrIterator 193 .next(); 194 try { 195 extendedContainer = extender.getExtendedContainer(); 196 } catch (IllegalActionException e) { 197 // Ignore the exception, and try the next extender. 198 } 199 } 200 201 if (extendedContainer == null) { 202 container = container.getContainer(); 203 } else { 204 container = extendedContainer; 205 } 206 } 207 } 208 209 return null; 210 } 211 212 /** Get the NamedObj with the given name in the scope of the given 213 * container. If the name contains the "::" scoping specifier, 214 * then an attribute more deeply in the hierarchy is searched 215 * for. If the specified container is lazy (implements 216 * LazyComposite), then references to its contained entities 217 * or relations will not resolve, so such references are disallowed. 218 * @param container The container to search upwards from. 219 * @param name The object name to search for. 220 * @return The NamedObj with the given name or null if the NamedObj 221 * does not exist. 222 */ 223 public static NamedObj getScopedObject(NamedObj container, String name) { 224 if (name.equals("this")) { 225 return container; 226 } 227 228 String[] parts = name.replaceAll("::", ".").split("\\."); 229 NamedObj result = null; 230 boolean lookup = true; 231 for (String part : parts) { 232 result = null; 233 while (container != null) { 234 //Attribute attribute = container.getAttribute(part); 235 Attribute attribute = _searchAttributeIn(null, container, part); 236 if (attribute != null) { 237 result = attribute; 238 } else { 239 if (container instanceof Entity) { 240 Port port = ((Entity) container).getPort(part); 241 if (port != null) { 242 result = port; 243 } else if (container instanceof CompositeEntity) { 244 // NOTE: Lazy composites cannot have references to their 245 // contained entities or relations. This would defeat the 246 // lazy mechanism, forcing the actor to populate its 247 // contents. 248 if (!(container instanceof LazyComposite)) { 249 ComponentEntity entity = ((CompositeEntity) container) 250 .getEntity(part); 251 if (entity != null) { 252 result = entity; 253 } else { 254 ComponentRelation relation = ((CompositeEntity) container) 255 .getRelation(part); 256 if (relation != null) { 257 result = relation; 258 } 259 } 260 } 261 } 262 } 263 } 264 if (lookup && result == null) { 265 List attributes = container 266 .attributeList(ContainmentExtender.class); 267 Iterator attrIterator = attributes.iterator(); 268 NamedObj extendedContainer = null; 269 while (extendedContainer == null 270 && attrIterator.hasNext()) { 271 ContainmentExtender extender = (ContainmentExtender) attrIterator 272 .next(); 273 try { 274 extendedContainer = extender.getExtendedContainer(); 275 } catch (IllegalActionException e) { 276 // Ignore the exception, and try the next extender. 277 } 278 } 279 280 if (extendedContainer == null) { 281 container = container.getContainer(); 282 } else { 283 container = extendedContainer; 284 } 285 } else { 286 break; 287 } 288 } 289 if (result == null) { 290 break; 291 } 292 container = result; 293 lookup = false; 294 } 295 296 return result; 297 } 298 299 /** Get the variable with the given name in the scope of the given 300 * container. If the name contains the "::" scoping specifier, 301 * then an attribute more deeply in the hierarchy is searched 302 * for. The scope of the object includes any container of the 303 * given object, and any variable contained in a scope extending 304 * attribute inside any of those containers. 305 * @param exclude A variable to exclude from the search. 306 * @param container The container to search upwards from. 307 * @param name The variable name to search for. 308 * @return The variable with the given name or null if the variable 309 * does not exist. 310 */ 311 public static Variable getScopedVariable(Variable exclude, 312 NamedObj container, String name) { 313 // This is the primary entry point for this class, used 314 // by the Expression actor and others. 315 String insideName = name.replaceAll("::", "."); 316 317 while (container != null) { 318 Variable result = _searchVariableIn(exclude, container, insideName); 319 320 if (result != null) { 321 return result; 322 } else { 323 List attributes = container 324 .attributeList(ContainmentExtender.class); 325 Iterator attrIterator = attributes.iterator(); 326 NamedObj extendedContainer = null; 327 while (extendedContainer == null && attrIterator.hasNext()) { 328 ContainmentExtender extender = (ContainmentExtender) attrIterator 329 .next(); 330 try { 331 extendedContainer = extender.getExtendedContainer(); 332 } catch (IllegalActionException e) { 333 // Ignore the exception, and try the next extender. 334 } 335 } 336 337 if (extendedContainer == null) { 338 container = container.getContainer(); 339 } else { 340 container = extendedContainer; 341 } 342 } 343 } 344 345 return null; 346 } 347 348 /** Check to see whether a preference of the specified name is 349 * defined in the specified context, and if it is, return its value. 350 * Note that if there is an error in the expression for the preference, 351 * then this method will return null and report the error to standard out. 352 * This is done because we assume the error will normally be caught 353 * before this method is called. 354 * @param context The context for the preference. 355 * @param preferenceName The name of the preference. 356 * @return The value of the preference, or null if it is not set. 357 */ 358 public static Token preferenceValue(NamedObj context, 359 String preferenceName) { 360 Variable result = ModelScope.getScopedVariable(null, context, 361 preferenceName); 362 363 if (result != null) { 364 try { 365 return result.getToken(); 366 } catch (IllegalActionException ex) { 367 System.out.println("Warning: Invalid preference: " + ex); 368 } 369 } 370 371 // If no scoped variable is found, try for a defined constant. 372 return Constants.get(preferenceName); 373 } 374 375 // Search in the container for an attribute with the given name. 376 // Search recursively in any instance of ScopeExtender in the 377 // container. 378 private static Attribute _searchAttributeIn(Attribute exclude, 379 NamedObj container, String name) { 380 Attribute result = container.getAttribute(name); 381 382 if (result != null && result != exclude) { 383 return result; 384 } else { 385 Iterator extenders = container.attributeList(ScopeExtender.class) 386 .iterator(); 387 388 while (extenders.hasNext()) { 389 ScopeExtender extender = (ScopeExtender) extenders.next(); 390 result = extender.getAttribute(name); 391 392 if (result != null && result != exclude) { 393 return result; 394 } 395 } 396 } 397 398 return null; 399 } 400 401 // Search in the container for a variable with the given name. 402 // Search recursively in any instance of ScopeExtender in the 403 // container. 404 private static Variable _searchVariableIn(Variable exclude, 405 NamedObj container, String name) { 406 Attribute result = container.getAttribute(name); 407 408 if (result != null && result instanceof Variable && result != exclude) { 409 return (Variable) result; 410 } else { 411 Iterator extenders = container.attributeList(ScopeExtender.class) 412 .iterator(); 413 414 while (extenders.hasNext()) { 415 ScopeExtender extender = (ScopeExtender) extenders.next(); 416 result = extender.getAttribute(name); 417 418 if (result != null && result instanceof Variable 419 && result != exclude) { 420 return (Variable) result; 421 } 422 } 423 } 424 425 return null; 426 } 427}