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