001/*
002 * Copyright (c) 2004-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: barseghian $'
006 * '$Date: 2010-12-22 01:17:53 +0000 (Wed, 22 Dec 2010) $' 
007 * '$Revision: 26584 $'
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
032/**
033 * 
034 */
035
036import java.net.URI;
037import java.util.Collections;
038import java.util.Iterator;
039import java.util.NoSuchElementException;
040import java.util.Set;
041import java.util.Vector;
042import java.util.regex.Matcher;
043import java.util.regex.Pattern;
044
045import org.apache.commons.logging.Log;
046import org.apache.commons.logging.LogFactory;
047import org.semanticweb.owl.apibinding.OWLManager;
048import org.semanticweb.owl.io.RDFXMLOntologyFormat;
049import org.semanticweb.owl.model.AddAxiom;
050import org.semanticweb.owl.model.OWLAnnotation;
051import org.semanticweb.owl.model.OWLAxiom;
052import org.semanticweb.owl.model.OWLClass;
053import org.semanticweb.owl.model.OWLDataFactory;
054import org.semanticweb.owl.model.OWLDescription;
055import org.semanticweb.owl.model.OWLLabelAnnotation;
056import org.semanticweb.owl.model.OWLOntology;
057import org.semanticweb.owl.model.OWLOntologyChangeException;
058import org.semanticweb.owl.model.OWLOntologyManager;
059import org.semanticweb.owl.model.OWLOntologyStorageException;
060import org.semanticweb.owl.model.OWLRuntimeException;
061import org.semanticweb.owl.vocab.OWLRDFVocabulary;
062
063public class NamedOntClass implements Comparable {
064
065        protected OWLClass _ontClass;
066        protected OWLOntology _ontology;
067        protected String _name;
068        protected NamedOntModel _model = null;
069        protected boolean _orphaned;
070        private boolean _removable = true;
071
072        public String getManualUrl() {
073                return _manualUrl;
074        }
075
076        public void setManualUrl(String _manualUrl) {
077                this._manualUrl = _manualUrl;
078        }
079
080        protected String _manualUrl;
081
082        public boolean isOrphaned() {
083                return _orphaned;
084        }
085
086        public void setOrphaned(boolean orphaned) {
087                this._orphaned = orphaned;
088        }
089
090        public NamedOntClass(String url, String label) {
091                this.setOrphaned(true);
092                this.setManualUrl(url);
093                _name = label;
094        }
095        
096        public NamedOntClass(String text) {
097                OntologyCatalog catalog = OntologyCatalog.instance();
098                _ontology = catalog.getDefaultOntology();
099                _name = text;
100                _ontClass = catalog.getConceptWithLabel(text);
101                _orphaned = false;
102                if (_ontClass == null) {
103                        // This isn't already in the default ontology, so we have to deal with that.
104                        OWLOntologyManager manager = OntologyCatalog.getManager();
105                        OWLDataFactory factory = manager.getOWLDataFactory();
106                        OWLClass cls = factory.getOWLClass(URI.create(_ontology.getURI() + makeNameIntoOWLClass(text)));
107                        OWLLabelAnnotation label = factory.getOWLLabelAnnotation(text);
108                        OWLAxiom axiom = factory.getOWLEntityAnnotationAxiom(cls, label);
109                        try {
110                                manager.applyChange(new AddAxiom(_ontology, axiom));
111                        }
112                        catch(OWLOntologyChangeException ex) {
113                                log.error("Error adding new class and label: " + text, ex);
114                        }
115                        try {
116                                // NOTE: Must specify a separate physical URI or it doesn't
117                                // NOTE: know where it is. The logical URI is no help.
118                                Iterator<NamedOntModel> iterator = catalog.getNamedOntModels();
119                                while (iterator.hasNext()) {
120                                        NamedOntModel model = iterator.next();
121                                        if (isDefaultModel(model)) {
122                                                manager.saveOntology(_ontology, new RDFXMLOntologyFormat(), model.getFile().toURI());
123                                                log.info("Saved ontology");
124                                                break;
125                                        }
126                                }
127                        }
128                        catch(OWLOntologyStorageException ex) {
129                                log.error("Error saving ontology", ex);
130                        }
131
132                        _ontClass = cls;
133                }
134        }
135
136        private void setModel() {
137                OntologyCatalog catalog = OntologyCatalog.instance();
138                Iterator<NamedOntModel> iterator = catalog.getNamedOntModels();
139                while(iterator.hasNext()) {
140                        NamedOntModel model = iterator.next();
141                        if (model.getOntology().equals(_ontology)) {
142                                _model = model;
143                        }
144                }
145                
146        }
147
148        public NamedOntClass getThisClass() {
149                return this;
150        }
151
152        private boolean isDefaultModel(NamedOntModel model) {
153                return Constants.defaultOntologyName.equals(model.getName());
154        }
155
156        private String makeNameIntoOWLClass(String text) {
157                return makeNameIntoOWLClassByAddingUnderscores(text);
158        }
159        
160        private String makeNameIntoOWLClassByAddingUnderscores(String text) {
161                StringBuilder buffer = new StringBuilder("#");
162                for (String chunk : text.split("\\s+")) {
163                        buffer.append(chunk).append("_");
164                }
165                String string = buffer.toString();
166                return string.substring(0, string.length() - 1);
167        }
168        
169        // This has been deprecated in favor of the whitespace replacement
170        // approach (see: makeNameIntoOWLClassByAddingUnderscores(String).
171/*
172        private String makeNameIntoOWLClassByRemovingWhitespace(String text) {
173                StringBuilder buffer = new StringBuilder("#");
174                for (String chunk : text.split("\\s+")) {
175                        buffer.append(chunk);
176                }
177                return buffer.toString();
178        }
179*/
180
181        public NamedOntClass(OWLClass ontClass, OWLOntology ontology) {
182                _ontClass = ontClass;
183                _ontology = ontology;
184                try {
185                        String str = NamedOntModel.parseLabel(ontClass.getAnnotations(ontology, OWLRDFVocabulary.RDFS_LABEL.getURI()).toArray(new OWLAnnotation[1])[0]);                
186                        if (str != null)
187                                _name = str;
188                        else
189                                _name = ontClass.toString();
190                }
191                catch(NullPointerException ex) {
192                        _name = ontClass.toString();
193                }
194        }
195        
196        public NamedOntClass(NamedOntClass noc) {
197                this._ontClass = noc.ontClass();
198                this._ontology = noc.getOntology();
199                this._name = noc.getName();
200        }
201
202        public boolean isRemovable() {
203                return _removable;
204        }
205        
206        public void setRemovable(boolean removable) {
207                _removable = removable;
208        }
209
210        public static class IteratorIterable<T> implements Iterable<T> {
211                public IteratorIterable(Iterator<T> iterator) {
212                        this.iterator = iterator;
213                }
214
215                public Iterator<T> iterator() {
216                        return this.iterator;
217                }
218
219                private Iterator<T> iterator;
220        }
221        
222        public static NamedOntClass createNamedOntClassFromURI(String tagURIString) {
223                String ontologyURIString;
224                String[] parts = tagURIString.split("#");
225                if (parts.length < 2) {
226                        return null;
227                }
228                else {
229                        ontologyURIString = parts[0];
230                }
231                return createNamedOntClassFromURIs(tagURIString, ontologyURIString);
232        }
233
234        public static NamedOntClass createNamedOntClassFromURIs(String tagURIString, String ontologyURIString) {
235                //String originalTagURIString = tagURIString;
236
237                String[] parts = tagURIString.split("#");
238                
239                String label;
240                if (parts.length > 2) {
241                        label = parts[2];
242                        tagURIString = parts[0] + "#" + parts[1];
243                }
244                else {
245                        label = parts[1];
246                }
247                OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
248                OWLClass cls = manager.getOWLDataFactory().getOWLClass(URI.create(tagURIString));
249                
250                NamedOntModel theModel = null;
251                for (NamedOntModel model : new IteratorIterable<NamedOntModel>(OntologyCatalog.instance().getNamedOntModels())) {
252                        if (ontologyURIString.equals(model.getOntology().getURI().toString())) {
253                                theModel = model;
254                        }
255                }
256                if (theModel == null) {
257                        // Model not found
258                        /// changed 12.21.10
259                        ///return new NamedOntClass(originalTagURIString, label);
260                        /// sometimes the originalTagURIString looks like (dont want extra label):
261                        /// urn:lsid:localhost:onto:797050988:1#testtag#testtag
262                        return new NamedOntClass(tagURIString, label);
263                }
264                
265                OWLOntology ontology = theModel.getOntology();
266                return new NamedOntClass(cls, ontology);
267        }
268
269        public String toString() {
270                return getName();
271        }
272
273        public OWLClass ontClass() {
274                return _ontClass;
275        }
276        
277        public OWLOntology getOntology() {
278                return _ontology;
279        }
280
281        public String getName() {
282                return _name;
283        }
284        
285        private static Pattern parseAnnotation = Pattern.compile("\"(.*)\"\\^\\^string");
286        
287        public String getComment() {
288                try {
289                        OWLAnnotation annotation = _ontClass.getAnnotations(_ontology, OWLRDFVocabulary.RDFS_COMMENT.getURI()).iterator().next();
290                        String rawAnnotation = annotation.getAnnotationValue().toString();
291                        Matcher matcher = parseAnnotation.matcher(rawAnnotation);
292                        if (matcher.matches()) {
293                                return matcher.group(1);
294                        }
295                        return rawAnnotation;
296                }
297                catch(NoSuchElementException ex) {
298                        return "";
299                }
300        }
301        
302        public NamedOntModel getModel() {
303                if (_model == null) {
304                        setModel();
305                }
306                return _model;
307        }
308
309        public String getOntologyName() {
310                String nspace = getNameSpace();
311                OntologyCatalog _catalog = OntologyCatalog.instance();
312                return _catalog.getOntologyName(nspace);
313        }
314
315        public String getLocalName() {
316                String uriString = getConceptUri();
317                return uriString.split("#")[1];
318//              return _ontClass.toString();
319        }
320        
321        public boolean hasNameSpace() {
322                String uriString = getConceptUri();
323//              String uriString = _ontClass.getURI().toString();
324                String localName = getLocalName();
325                assert(uriString.endsWith(localName));
326                return uriString.length() - localName.length() > 1;
327        }
328        
329        private String getConceptUri() {
330                if (isOrphaned()) {
331                        return getManualUrl();
332                }
333                else {
334                        return _ontClass.getURI().toString();
335                }               
336        }
337        
338        public String getNameSpace() {
339                if (hasNameSpace()) {
340                        return _getNameSpace();
341                }
342                else {
343                        return _ontology.getURI().toString() + "#";
344                }
345        }
346
347        public String _getNameSpace() {
348                String uriString = getConceptUri();
349                String localName = getLocalName();
350                assert(uriString.endsWith(localName));
351                return uriString.substring(0, uriString.length() - localName.length());
352        }
353
354        public String getConceptId() {
355                return getNameSpace() + getLocalName();
356        }
357        
358        public String getConceptIdWithLabel() {
359                return getConceptId() + "#" + getName();
360        }
361
362        /**
363         * @param sorted
364         *            Return sorted list if true.
365         */
366        public Iterator<NamedOntClass> getNamedSubClasses(boolean sorted) {
367                Vector<NamedOntClass> result = new Vector<NamedOntClass>();
368                for (OWLDescription desc : _ontClass.getSubClasses(_ontology)) {
369                        OWLClass subcls = desc.asOWLClass();
370                        boolean hasLabel = !subcls.getAnnotations(_ontology, OWLRDFVocabulary.RDFS_LABEL.getURI()).isEmpty();
371                        if (hasLabel || subcls.toString() != null) {
372                                // NOTE: Does subcls.toString == null happen?
373                                result.add(new NamedOntClass(subcls, _ontology));
374                        }
375                }
376                if (sorted) {
377                        Collections.sort(result);
378                }
379                return result.iterator();
380        }
381
382        /**
383         * @param sorted
384         *            Return sorted list if true.
385         */
386        public Iterator<NamedOntClass> getNamedSuperClasses(boolean sorted) {
387                Vector<NamedOntClass> result = new Vector<NamedOntClass>();
388                for (OWLDescription desc : _ontClass.getSuperClasses(_ontology)) {
389                        OWLClass supcls;
390                        try {
391                                supcls = desc.asOWLClass();
392                        }
393                        catch(OWLRuntimeException ex) {
394                                log.warn("Failed to parse: " + desc + " (" + desc.getClass().getName() + ")");
395                                continue;
396                        }
397                        boolean hasLabel = !supcls.getAnnotations(_ontology, OWLRDFVocabulary.RDFS_LABEL.getURI()).isEmpty();
398                        if (hasLabel || supcls.toString() != null) {
399                                // NOTE: Does supcls.toString == null happen?
400                                result.add(new NamedOntClass(supcls, _ontology));
401                        }
402                }
403                if (sorted) {
404                        Collections.sort(result);
405                }
406                return result.iterator();
407        }
408
409        public Iterator<NamedOntProperty> getNamedProperties(boolean sorted) {
410                return Collections.<NamedOntProperty>emptyList().iterator();
411//              Vector<NamedOntProperty> result = new Vector<NamedOntProperty>();
412//
413//              Set<OWLAnnotation> annotations = _ontClass.getAnnotations(_ontology);
414////            for (OWLAnnotation annotation : annotations) {
415////                    annotation.getAnno
416////            }
417//              for (Iterator<OWLProperty> iter = _ontClass.listDeclaredProperties(true); iter
418//                              .hasNext();) {
419//                      OWLProperty prop = (OWLProperty) iter.next();
420//                      result.add(new NamedOntProperty(prop));
421//              }
422//              if (sorted)
423//                      Collections.sort(result);
424//              return result.iterator();
425        }
426
427//      /**
428//       * @return The (first) comment associated with the class
429//       */
430//      // NOTE: Not used
431//      public String getComment() {
432//              return _ontClass.getComment(null);
433//      }
434
435//      /**
436//       * @return Answer true if the given class is a "direct" sub-class of this
437//       *         class.
438//       */
439//      // NOTE: Not used - but would be easy to convert
440//      public boolean isDirectSubClass(NamedOntClass cls) {
441//              for (Iterator<NamedOntClass> iter = getNamedSubClasses(false); iter.hasNext();) {
442//                      NamedOntClass tmpCls = (NamedOntClass) iter.next();
443//                      if (tmpCls.equals(cls))
444//                              return true;
445//              }
446//              return false;
447//      }
448
449        /**
450         * @return Answer true if the given class is a sub-class of this class.
451         */
452        public boolean isSubClass(NamedOntClass cls) {
453                // return _ontClass.hasSubClass(cls.ontClass(), false);
454                Set<OWLDescription> descriptions = this.ontClass().getSubClasses(this.getOntology());
455                for (OWLDescription description : descriptions) {
456                        NamedOntClass newClass = new NamedOntClass(description.asOWLClass(), this.getOntology());
457                        if (cls.equals(newClass)) {
458                                return true;
459                        }
460                }
461                return false;
462        }
463
464//      /**
465//       * @return Answer true if the given class is a "direct" super-class of this
466//       *         class.
467//       */
468//      // NOTE: Not used - but would be easy to convert
469//      public boolean isDirectSuperClass(NamedOntClass cls) {
470//              // return _ontClass.hasSuperClass(cls.ontClass(), true);
471//              for (Iterator<NamedOntClass> iter = getNamedSuperClasses(false); iter.hasNext();) {
472//                      NamedOntClass tmpCls = (NamedOntClass) iter.next();
473//                      if (tmpCls.equals(cls))
474//                              return true;
475//              }
476//              return false;
477//      }
478
479//      /**
480//       * @return Answer true if the given class is a super-class of this class.
481//       */
482//      // NOTE: Not used - but would be easy to convert
483//      public boolean isSuperClass(NamedOntClass cls) {
484//              // return _ontClass.hasSuperClass(cls.ontClass(), false);
485//              for (Iterator<NamedOntClass> iter = getNamedSuperClasses(false); iter.hasNext();) {
486//                      NamedOntClass tmpCls = (NamedOntClass) iter.next();
487//                      if (tmpCls.equals(cls))
488//                              return true;
489//                      if (tmpCls.isSuperClass(cls))
490//                              return true;
491//              }
492//              return false;
493//      }
494
495        /**
496         * @return Answer true if the given class is disjoint with this class.
497         */
498        // NOTE: Not used
499        public boolean isDisjointWith(NamedOntClass cls) {
500                Set<OWLDescription> descriptions = ontClass().getDisjointClasses(this.getOntology());
501                for (OWLDescription description : descriptions) {
502                        NamedOntClass newClass = new NamedOntClass(description.asOWLClass(), this.getOntology());
503                        if (cls.equals(newClass)) {
504                                return true;
505                        }
506                }
507                return false;
508        }
509
510        /**
511         * @return Answer true if the given class is equivalent to this class.
512         */
513        // NOTE: Not used
514        public boolean isEquivalent(NamedOntClass cls) {
515                Set<OWLDescription> descriptions = ontClass().getEquivalentClasses(this.getOntology());
516                for (OWLDescription description : descriptions) {
517                        NamedOntClass newClass = new NamedOntClass(description.asOWLClass(), this.getOntology());
518                        if (cls.equals(newClass)) {
519                                return true;
520                        }
521                }
522                return false;
523        }
524
525        public boolean equals(Object obj) {
526                if (!(obj instanceof NamedOntClass))
527                        return false;
528                NamedOntClass tmp = (NamedOntClass) obj;
529                String myUri, yourUri;
530                myUri = this.isOrphaned() ? this.getManualUrl() : this.getAbsoluteURI().toString();
531                yourUri = tmp.isOrphaned() ? tmp.getManualUrl() : tmp.getAbsoluteURI().toString();
532                return myUri.equals(yourUri);
533        }
534
535        @Override
536        public int hashCode() {
537                if (isOrphaned()) {
538                        return getManualUrl().hashCode();
539                }
540                return getAbsoluteURI().hashCode();
541        }
542
543        private URI getAbsoluteURI() {
544                // Make OWL class absolute
545                URI absoluteOntClassURI;
546                if (_ontClass.getURI().toString().startsWith("#")) {
547                        absoluteOntClassURI = URI.create(_ontology.getURI().toString() + _ontClass.getURI().toString());
548                }
549                else {
550                        absoluteOntClassURI = _ontClass.getURI();
551                }
552                
553                return absoluteOntClassURI;
554        }
555
556        public int compareTo(Object obj) {
557                String str1 = toString();
558                String str2 = obj.toString();
559                return str1.compareTo(str2);
560        }
561        
562        private static final Log log = LogFactory.getLog(NamedOntClass.class);  
563
564}