001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2012-11-26 22:23:53 +0000 (Mon, 26 Nov 2012) $' 007 * '$Revision: 31128 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.sms; 031 032import java.util.ArrayList; 033import java.util.Iterator; 034import java.util.List; 035import java.util.Vector; 036 037import org.kepler.moml.NamedObjId; 038 039import ptolemy.actor.IOPort; 040import ptolemy.actor.TypedIOPort; 041import ptolemy.kernel.Entity; 042import ptolemy.kernel.util.IllegalActionException; 043import ptolemy.kernel.util.NameDuplicationException; 044import ptolemy.kernel.util.NamedObj; 045 046////////////////////////////////////////////////////////////////////////// 047//// SMSServices 048/** 049 * This class provides a set of operations, or services for interacting with the 050 * Semantic Mediation System. These services are currently only partially 051 * defined. 052 * 053 * @author Shawn Bowers 054 * @version $Id: SMSServices.java 31128 2012-11-26 22:23:53Z crawl $ 055 * @since Kepler alpha 056 */ 057 058public class SMSServices { 059 060 public static int COMPATIBLE = 1; 061 public static int UNKNOWN = 0; 062 public static int INCOMPATIBLE = -1; 063 064 private static List<SMSTagChangeListener> listeners = new ArrayList<SMSTagChangeListener>(); 065 066 public static void addListener(SMSTagChangeListener listener) { 067 listeners.add(listener); 068 } 069 070 public static void removeListener(SMSTagChangeListener listener) { 071 listeners.remove(listener); 072 } 073 074 /** 075 * For use within an extended SCIA tool 076 */ 077 // public String getSemanticCorrespondences(Actor source, Actor target) 078 079 /** 080 * Compare the compatibility of the two sets of semantic types. Both 081 * arguements represent conjoined sets of semantic types. The method returns 082 * three possible values: compatible, unknown, or incompatible. If either 083 * type is empty, unknown is returned. The types are considered compatible 084 * if they are non-empty and if the conjunction of the first set implies the 085 * conjunction of the second set. The types are considered incompatible if 086 * they are not compatible and not unkown. 087 * 088 * @param semSubtypes 089 * The semantic types that when conjoined form a sub-class of the 090 * super type (semSupertypes). 091 * @param semSupertypes 092 * The semantic types that when conjoined form a super-class of 093 * the sub type (subSemTypes) 094 * @return Answers {@link #COMPATIBLE} if the inputs are compatible, 095 * {@link #UNKNOWN} if the inputs are unkown, and 096 * {@link INCOMPATIBLE} if the types are incompatible. 097 * 098 * FIXME: Need to somehow handle the case when the semtype is not 099 * available locally: Can we assume they are known here? Do we need 100 * to call the object manager? Do we throw an exception? For now, we 101 * just ignore them! 102 */ 103 public static int compare(Vector semSubtypes, Vector semSupertypes) { 104 OntologyCatalog catalog = OntologyCatalog.instance(); 105 Vector<NamedOntClass> subClasses = new Vector<NamedOntClass>(); 106 Vector<NamedOntClass> superClasses = new Vector<NamedOntClass>(); 107 108 // first check if either is empty; and if so return unknown 109 if (semSubtypes.size() == 0 || semSupertypes.size() == 0) 110 return UNKNOWN; 111 112 // convert to ont classes; if we don't have knowledge of the 113 // class, then don't add it ... 114 for (Iterator iter = semSubtypes.iterator(); iter.hasNext();) { 115 NamedOntClass c = catalog.getNamedOntClass((SemanticType) iter 116 .next()); 117 if (c != null && !subClasses.contains(c)) 118 subClasses.add(c); // ignore unknown types 119 } 120 for (Iterator iter = semSupertypes.iterator(); iter.hasNext();) { 121 NamedOntClass c = catalog.getNamedOntClass((SemanticType) iter 122 .next()); 123 if (c != null && !superClasses.contains(c)) 124 superClasses.add(c); // ignore unkown types 125 } 126 127 // if we don't have any classes, return unknown 128 if (subClasses.size() == 0 || superClasses.size() == 0) 129 return UNKNOWN; 130 131 // if the sem-subtypes contain a contradiction, then return 132 // compatible (i.e., false implies anything) 133 for (Iterator<NamedOntClass> iter = subClasses.iterator(); iter.hasNext();) { 134 NamedOntClass cls = iter.next(); 135 for (Iterator<NamedOntClass> iter2 = subClasses.iterator(); iter.hasNext();) { 136 NamedOntClass tstCls = iter.next(); 137 if (cls.isDisjointWith(tstCls)) 138 return COMPATIBLE; 139 } 140 } 141 142 // if the sem-supertypes contain a contradiction, then return 143 // incompatible, (i.e., we have true implies false) 144 for (Iterator<NamedOntClass> iter = superClasses.iterator(); iter.hasNext();) { 145 NamedOntClass cls = iter.next(); 146 for (Iterator<NamedOntClass> iter2 = superClasses.iterator(); iter.hasNext();) { 147 NamedOntClass tstCls = iter.next(); 148 if (cls.isDisjointWith(tstCls)) 149 return INCOMPATIBLE; 150 } 151 } 152 153 // check that every supertype has a corresponding subtype 154 for (Iterator<NamedOntClass> iter = superClasses.iterator(); iter.hasNext();) { 155 NamedOntClass superClass = iter.next(); 156 boolean found = false; 157 for (Iterator<NamedOntClass> iter2 = subClasses.iterator(); iter2.hasNext();) { 158 NamedOntClass subClass = iter2.next(); 159 if (superClass.isEquivalent(subClass) 160 || superClass.isSubClass(subClass) || superClass.equals(subClass)) 161 found = true; 162 } 163 if (!found) 164 return INCOMPATIBLE; 165 } 166 167 return COMPATIBLE; 168 } 169 170 /** 171 * 172 */ 173 public static boolean compatible(Vector semSubtypes, Vector semSupertypes) { 174 return compare(semSubtypes, semSupertypes) == COMPATIBLE; 175 } 176 177 /** 178 * 179 */ 180 public static NamedOntClass getNamedOntClassFor(SemanticType semtype) { 181 OntologyCatalog catalog = OntologyCatalog.instance(); 182 return catalog.getNamedOntClass(semtype); 183 } 184 185 /** 186 * 187 */ 188 public static NamedOntClass getNamedOntClassFor(String conceptId) { 189 OntologyCatalog catalog = OntologyCatalog.instance(); 190 return catalog.getNamedOntClass(conceptId); 191 } 192 193 /** 194 * 195 */ 196 public static Vector<SemanticType> getActorSemanticTypes(NamedObj obj) { 197 Vector<SemanticType> result = new Vector<SemanticType>(); 198 List<SemanticType> semTypes = obj.attributeList(SemanticType.class); 199 Iterator<SemanticType> iter = semTypes.iterator(); 200 while (iter.hasNext()) 201 result.add(iter.next()); 202 return result; 203 } 204 205 /** 206 * Set given NamedObj's SemanticTypes to namedOntClasses 207 * 208 * @param obj 209 * The named object (actor) to set the types on 210 * @param namedOntClasses 211 * Set named object's SemanticTypes to these 212 */ 213 public static void setActorSemanticTypes(NamedObj obj, Vector<NamedOntClass> namedOntClasses) { 214 215 216 final List<NamedOntClass> removedClasses = new ArrayList<NamedOntClass>(); 217 final List<NamedOntClass> addedClasses = new ArrayList<NamedOntClass>(); 218 219 // make sure obj is not null 220 if (obj == null){ 221 return; 222 } 223 // make sure namedOntClasses is not null; is so create an empty vector 224 if (namedOntClasses == null){ 225 namedOntClasses = new Vector<NamedOntClass>(); 226 } 227 228 Iterator<SemanticType> existingItr = getActorSemanticTypes(obj).iterator(); 229 Iterator<NamedOntClass> namedOntItr = namedOntClasses.iterator(); 230 231 // ADD semTypes that aren't already there (those in namedOntClasses and not in existingSemTypes) 232 while (namedOntItr.hasNext()) { 233 boolean semTypeAlreadyExists = false; 234 NamedOntClass cls = namedOntItr.next(); 235 SemanticType s = null; 236 while (existingItr.hasNext()){ 237 s = existingItr.next(); 238 if (s.getConceptId().equals(cls.getConceptId())){ 239 semTypeAlreadyExists = true; 240 break; 241 } 242 } 243 244 if (!semTypeAlreadyExists){ 245 try { 246 SemanticType st = new SemanticType(obj, obj 247 .uniqueName("semanticType")); 248 if (cls != null) { 249 st.setConceptId(cls.getConceptIdWithLabel()); 250 ///12.21.10 Would this be more appropriate?: 251 ///st.setConceptId(cls.getConceptId()); 252 ///st.setLabel(cls.getName()); 253 254 // update LSID revision 255 NamedObjId noi = NamedObjId.getIdAttributeFor(obj); 256 if (noi != null) { 257 noi.updateRevision(); 258 } 259 260 addedClasses.add(cls); 261 262 } else 263 System.out 264 .println(">>> [SMSServices, 238] Cannot find class: " 265 + cls); 266 } catch (Exception e) { 267 e.printStackTrace(); 268 } 269 } 270 } 271 272 273 // REMOVE semantic types if necessary (those in existing, but not in namedOntClasses) 274 existingItr = getActorSemanticTypes(obj).iterator(); 275 while (existingItr.hasNext()){ 276 SemanticType s = existingItr.next(); 277 namedOntItr = namedOntClasses.iterator(); 278 279 boolean needToRemove = true; 280 while (namedOntItr.hasNext()) { 281 NamedOntClass cls = namedOntItr.next(); 282 /// 12.21.10 Instead, should the label be utilized? ala: 283 ///if (s.getConceptId().equals(cls.getConceptIdWithLabel())){ 284 if (s.getConceptUri().equals(cls.getConceptId())){ 285 needToRemove = false; 286 } 287 } 288 if (needToRemove){ 289 try { 290 s.setContainer(null); 291 // update LSID revision 292 NamedObjId noi = NamedObjId.getIdAttributeFor(obj); 293 if (noi != null) { 294 noi.updateRevision(); 295 } 296 removedClasses.add(NamedOntClass.createNamedOntClassFromURI(s.getConceptId())); 297 } catch (IllegalActionException e) { 298 // TODO Auto-generated catch block 299 e.printStackTrace(); 300 } catch (NameDuplicationException e) { 301 // TODO Auto-generated catch block 302 e.printStackTrace(); 303 } catch (Exception e) { 304 // TODO Auto-generated catch block 305 e.printStackTrace(); 306 } 307 } 308 } 309 310 // Right before we leave this method, let's notify the listeners 311 for (SMSTagChangeListener listener : listeners) { 312 listener.tagsChanged(obj, addedClasses, removedClasses); 313 } 314 } 315 316 /** 317 * Get all the ports, both "real" and "virtual" from a given named object. 318 * 319 * @param obj 320 * the named object 321 * @return the set of ports for the object TODO: Return as IOPortWrapper 322 * objects 323 */ 324 public static Vector<Object> getAllPorts(NamedObj obj) { 325 Vector<Object> result = new Vector<Object>(); 326 Iterator objIter = obj.containedObjectsIterator(); 327 while (objIter.hasNext()) { 328 Object item = objIter.next(); 329 if (item instanceof KeplerRefinementIOPort) 330 result.add(item); 331 else if (item instanceof TypedIOPort) 332 result.add(item); 333 else if (item instanceof KeplerCompositeIOPort) 334 result.add(item); 335 } 336 return result; 337 } 338 339 public static Vector<Object> getAllOutputPorts(NamedObj obj) { 340 Vector<Object> results = new Vector<Object>(); 341 Iterator<Object> portIter = getAllPorts(obj).iterator(); 342 while (portIter.hasNext()) { 343 Object p = portIter.next(); 344 if (p instanceof KeplerCompositeIOPort) { 345 if (((KeplerCompositeIOPort) p).isOutput()) 346 results.add(p); 347 } else if (p instanceof KeplerRefinementIOPort) { 348 if (((KeplerRefinementIOPort) p).isOutput()) 349 results.add(p); 350 } else if (p instanceof IOPort) { 351 if (((IOPort) p).isOutput()) 352 results.add(p); 353 } 354 } 355 return results; 356 } 357 358 public static Vector<Object> getAllInputPorts(NamedObj obj) { 359 Vector<Object> results = new Vector<Object>(); 360 Iterator<Object> portIter = getAllPorts(obj).iterator(); 361 while (portIter.hasNext()) { 362 Object p = portIter.next(); 363 if (p instanceof KeplerCompositeIOPort) { 364 if (((KeplerCompositeIOPort) p).isInput()) 365 results.add(p); 366 } else if (p instanceof KeplerRefinementIOPort) { 367 if (((KeplerRefinementIOPort) p).isInput()) 368 results.add(p); 369 } else if (p instanceof IOPort) { 370 if (((IOPort) p).isInput()) 371 results.add(p); 372 } 373 } 374 return results; 375 } 376 377 /** 378 * Get all the io ports 379 * 380 * @param obj 381 * the named object 382 * @return the set of ports for the object TODO: Return as IOPortWrapper 383 * objects 384 */ 385 public static Vector<Object> getIOPorts(NamedObj obj) { 386 Vector<Object> result = new Vector<Object>(); 387 Iterator objIter = obj.containedObjectsIterator(); 388 while (objIter.hasNext()) { 389 Object item = objIter.next(); 390 if (item instanceof IOPort) 391 result.add(item); 392 } 393 return result; 394 } 395 396 /** 397 * Get all the existing refinement ports defiend for a given named object. 398 * 399 * @param obj 400 * the named object 401 * @return the set of ports for the object TODO: Return as IOPortWrapper 402 * objects 403 */ 404 public static Vector<Object> getRefinementPorts(NamedObj obj) { 405 Vector<Object> result = new Vector<Object>(); 406 Iterator objIter = obj.containedObjectsIterator(); 407 while (objIter.hasNext()) { 408 Object item = objIter.next(); 409 if (item instanceof KeplerRefinementIOPort) 410 result.add(item); 411 } 412 return result; 413 } 414 415 /** 416 * Get all the port bundles define for a given named object. 417 * 418 * @param obj 419 * the named object 420 * @return the set of ports for the object TODO: Return as IOPortWrapper 421 * objects 422 */ 423 public static Vector<Object> getPortBundles(NamedObj obj) { 424 Vector<Object> result = new Vector<Object>(); 425 Iterator objIter = obj.containedObjectsIterator(); 426 while (objIter.hasNext()) { 427 Object item = objIter.next(); 428 if (item instanceof KeplerCompositeIOPort) 429 result.add(item); 430 } 431 return result; 432 } 433 434 /** 435 * Given a port object (TODO: convert to IOPortWrapper), returns the 436 * semantic types assigned to the port. 437 * 438 * @param port 439 * the port object 440 * @return a set of semantic type objects 441 */ 442 public static Vector<SemanticType> getPortSemanticTypes(Object port) { 443 Vector<SemanticType> result = new Vector<SemanticType>(); 444 Iterator<SemanticType> portIter = null; 445 if (port instanceof IOPort) 446 portIter = ((IOPort) port).attributeList(SemanticType.class) 447 .iterator(); 448 else if (port instanceof KeplerRefinementIOPort) 449 portIter = ((KeplerRefinementIOPort) port).attributeList( 450 SemanticType.class).iterator(); 451 else if (port instanceof KeplerCompositeIOPort) 452 portIter = ((KeplerCompositeIOPort) port).attributeList( 453 SemanticType.class).iterator(); 454 455 if (portIter == null) 456 return result; 457 458 while (portIter.hasNext()) 459 result.add(portIter.next()); 460 461 return result; 462 } 463 464 /** 465 * Return all the input semantic types for the object. 466 */ 467 public static Vector<SemanticType> getAllOutputSemanticTypes(NamedObj obj) { 468 Vector<SemanticType> result = new Vector<SemanticType>(); 469 Iterator<Object> portIter = getAllOutputPorts(obj).iterator(); 470 while (portIter.hasNext()) { 471 Object port = portIter.next(); 472 Iterator<SemanticType> typeIter = null; 473 if (port instanceof KeplerRefinementIOPort) 474 typeIter = SMSServices.getPortSemanticTypes( 475 (KeplerRefinementIOPort) port).iterator(); 476 else if (port instanceof KeplerCompositeIOPort) 477 typeIter = SMSServices.getPortSemanticTypes( 478 (KeplerCompositeIOPort) port).iterator(); 479 else if (port instanceof IOPort) 480 typeIter = SMSServices.getPortSemanticTypes((IOPort) port) 481 .iterator(); 482 while (typeIter.hasNext()) { 483 SemanticType t = typeIter.next(); 484 if (!result.contains(t)) 485 result.add(t); 486 } 487 } 488 return result; 489 } 490 491 /** 492 * Return all the input semantic types for the object. 493 */ 494 public static Vector<SemanticType> getAllInputSemanticTypes(NamedObj obj) { 495 Vector<SemanticType> result = new Vector<SemanticType>(); 496 Iterator<Object> portIter = getAllInputPorts(obj).iterator(); 497 while (portIter.hasNext()) { 498 Object port = portIter.next(); 499 Iterator<SemanticType> typeIter = null; 500 if (port instanceof KeplerRefinementIOPort) 501 typeIter = SMSServices.getPortSemanticTypes( 502 (KeplerRefinementIOPort) port).iterator(); 503 else if (port instanceof KeplerCompositeIOPort) 504 typeIter = SMSServices.getPortSemanticTypes( 505 (KeplerCompositeIOPort) port).iterator(); 506 else if (port instanceof IOPort) 507 typeIter = SMSServices.getPortSemanticTypes((IOPort) port) 508 .iterator(); 509 while (typeIter.hasNext()) { 510 SemanticType t = typeIter.next(); 511 if (!result.contains(t)) 512 result.add(t); 513 } 514 } 515 return result; 516 } 517 518 /** 519 * 520 */ 521 public static String exportSemanticAnnotation(Entity entity) { 522 // We assume that annotation rules always have the same 523 // parents. For example, given a port p1 :: {a = int, b = 524 // int} we have the following: 525 // 526 // Annotating p1 with #m gives 527 // val: $1 p1 => inst: $1 #m 528 // 529 // Annotating p1/a with #b gives 530 // val: $1 p1, val: $2 $1/a => inst: $2 #b 531 // 532 // Annotation p1 linked to p1/2 via #p gives 533 // val: $1 p1, val: $2 $1/a => prop: $1 #p $2 534 // 535 // Annotating generalization g1(p1/a, p1/b) with #d gives 536 // val: $1 p1, val: $2 $1/a, val: $3 $1/b => 537 // inst: g1($2, $3) #d 538 // 539 // where a and b are assumed to be values within the same 540 // port value 541 // 542 // Thus, we can't do "cross-product-style" annotation, e.g., 543 // given p1 with structural type {{a=int, b=int}} (a list of 544 // records), we can't say things like: 545 // 546 // val: $1 p1, val: $2 $1/elem, val: $3 $1/elem, 547 // val: $4 $2/a, val: $5 $3/b => 548 // inst: g1($4, $5) #d 549 // 550 // In particular, we would have instead: 551 // 552 // val: $1 p1, val: $2 $1/elem, val: $3 $2/a, 553 // val: $4 $2/b => 554 // inst: g1($3, $4) #d 555 // 556 // That is, we assume that the components always have 557 // the same parent 558 // 559 return null; 560 } 561 562 /** 563 * 564 */ 565 public static void importSemanticAnnotation(String annotation, Entity entity) { 566 } 567 568 // searching ... 569 570} // SMSServices