001/**
002 * Copyright (C) 2007 - 2016, Jens Lehmann
003 *
004 * This file is part of DL-Learner.
005 *
006 * DL-Learner is free software; you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation; either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * DL-Learner is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package org.dllearner.utilities;
020
021import com.clarkparsia.owlapiv3.XSD;
022import com.google.common.collect.Iterables;
023import com.google.common.collect.Sets;
024import org.apache.commons.lang3.Range;
025import org.dllearner.core.AbstractReasonerComponent;
026import org.dllearner.utilities.owl.SimpleOWLEntityChecker;
027import org.jetbrains.annotations.NotNull;
028import org.joda.time.DateTime;
029import org.joda.time.format.*;
030import org.semanticweb.owlapi.apibinding.OWLManager;
031import org.semanticweb.owlapi.manchestersyntax.parser.ManchesterOWLSyntaxClassExpressionParser;
032import org.semanticweb.owlapi.manchestersyntax.parser.ManchesterOWLSyntaxParserException;
033import org.semanticweb.owlapi.model.*;
034import org.semanticweb.owlapi.vocab.XSDVocabulary;
035import org.slf4j.Logger;
036import uk.ac.manchester.cs.owl.owlapi.OWLDatatypeImpl;
037
038import java.util.*;
039import java.util.function.Supplier;
040
041/**
042 * A collection of utility methods for the OWL API.
043 *
044 * @author Lorenz Buehmann
045 *
046 */
047public class OWLAPIUtils {
048        
049        private static final OWLCLassExpressionToOWLClassTransformer OWL_CLASS_TRANSFORM_FUNCTION = new OWLCLassExpressionToOWLClassTransformer();
050
051        public static final OWLOntologyManager  manager = OWLManager.createOWLOntologyManager();
052        public static final OWLDataFactory factory = manager.getOWLDataFactory();
053        
054    public final static Set<OWLDatatype> intDatatypes = new TreeSet<>(Arrays.asList(
055                        XSD.INT,
056                        XSD.INTEGER,
057                        XSD.POSITIVE_INTEGER,
058                        XSD.NEGATIVE_INTEGER,
059                        XSD.NON_POSITIVE_INTEGER,
060                        XSD.NON_NEGATIVE_INTEGER,
061                        XSD.SHORT,
062                        XSD.BYTE,
063                        XSD.UNSIGNED_INT,
064                        XSD.UNSIGNED_LONG
065        ));
066    public final static Set<OWLDatatype> floatDatatypes = new TreeSet<>(Arrays.asList(
067                        XSD.FLOAT,
068                        XSD.DOUBLE,
069                        factory.getOWLDatatype(XSDVocabulary.DECIMAL.getIRI())
070
071        ));
072    public final static Set<OWLDatatype> fixedDatatypes = new TreeSet<>(Collections.singletonList(
073                        XSD.BOOLEAN
074        ));
075    
076        /**
077         * The OWL 2 datatypes for the representation of time instants with and
078         * without time zone offsets.
079         */
080    public final static Set<OWLDatatype> owl2TimeDatatypes = Sets.newTreeSet(Arrays.asList(
081                        factory.getOWLDatatype(XSDVocabulary.DATE_TIME.getIRI()),
082                        factory.getOWLDatatype(XSDVocabulary.DATE_TIME_STAMP.getIRI())
083        ));
084    
085    public final static Set<OWLDatatype> dtDatatypes = Sets.newTreeSet(Arrays.asList(
086        XSD.DATE,
087        XSD.DATE_TIME,
088        XSD.G_DAY,
089        XSD.G_MONTH,
090        XSD.G_YEAR
091    ));
092    
093    public final static Set<OWLDatatype> periodDatatypes = Sets.newTreeSet(Arrays.asList(
094                XSD.DURATION,
095                new OWLDatatypeImpl(IRI.create(org.apache.jena.vocabulary.XSD.getURI() + "yearMonthDuration")),
096                new OWLDatatypeImpl(IRI.create(org.apache.jena.vocabulary.XSD.getURI() + "dayTimeDuration"))
097        ));
098        
099        public static final Set<OWLDatatype> numericDatatypes = Sets.union(intDatatypes, floatDatatypes);
100        
101        private static final Map<OWLDatatype, Class<?>> javaTypeMap;
102        static {
103                javaTypeMap = new TreeMap<>();
104                javaTypeMap.put(XSD.BYTE, Byte.class);
105                javaTypeMap.put(XSD.SHORT, Short.class);
106                javaTypeMap.put(factory.getOWLDatatype(XSDVocabulary.DECIMAL.getIRI()), Double.class);
107                javaTypeMap.put(XSD.INT, Integer.class);
108                javaTypeMap.put(XSD.INTEGER, Integer.class);
109                javaTypeMap.put(XSD.POSITIVE_INTEGER, Integer.class);
110                javaTypeMap.put(XSD.NEGATIVE_INTEGER, Integer.class);
111                javaTypeMap.put(XSD.NON_NEGATIVE_INTEGER, Integer.class);
112                javaTypeMap.put(XSD.NON_POSITIVE_INTEGER, Integer.class);
113                javaTypeMap.put(XSD.LONG, Long.class);
114                javaTypeMap.put(XSD.DOUBLE, Double.class);
115                javaTypeMap.put(XSD.FLOAT, Float.class);
116                javaTypeMap.put(XSD.BOOLEAN     , Boolean.class);
117                //javaTypeMap.put(OWL2Datatype.XSD_STRING, String.class);
118                //javaTypeMap.put(OWL2Datatype.XSD_, .class);
119        }
120        
121        public static final Map<OWLDatatype, DateTimeFormatter> dateTimeFormatters = new HashMap<>();
122        static {
123                dateTimeFormatters.put(XSD.G_YEAR, ISODateTimeFormat.year());
124                dateTimeFormatters.put(XSD.G_YEAR_MONTH, ISODateTimeFormat.yearMonth());
125                dateTimeFormatters.put(XSD.G_MONTH, DateTimeFormat.forPattern("--MM").withZoneUTC());
126                dateTimeFormatters.put(XSD.G_MONTH_DAY, DateTimeFormat.forPattern("--MM-DD").withZoneUTC());
127                dateTimeFormatters.put(XSD.G_DAY, DateTimeFormat.forPattern("---DD").withZoneUTC());
128                dateTimeFormatters.put(XSD.TIME, DateTimeFormat.forPattern("hh:mm:ss.sss").withOffsetParsed());
129                dateTimeFormatters.put(XSD.DATE, ISODateTimeFormat.date());
130                dateTimeFormatters.put(XSD.DATE_TIME, ISODateTimeFormat.dateHourMinuteSecond()); //  .dateTimeNoMillis());
131                dateTimeFormatters.put(factory.getOWLDatatype(XSDVocabulary.DATE_TIME_STAMP.getIRI()), ISODateTimeFormat.dateTimeNoMillis().withOffsetParsed());
132        }
133
134        public static final Map<OWLDatatype, DateTimeFormatter> dateTimeParsers = new HashMap<>(dateTimeFormatters);
135        static {
136//              dateTimeParsers.put(XSD.G_YEAR, ISODateTimeFormat.year());
137//              dateTimeParsers.put(XSD.G_YEAR_MONTH, ISODateTimeFormat.yearMonth());
138                dateTimeParsers.put(XSD.G_MONTH, new DateTimeFormatterBuilder()
139                .append(DateTimeFormat.forPattern("--MM"))
140                .appendOptional(DateTimeFormat.forPattern("Z").getParser())
141                .toFormatter().withZoneUTC());
142                dateTimeParsers.put(XSD.G_MONTH_DAY, new DateTimeFormatterBuilder()
143                .append(DateTimeFormat.forPattern("--MM-DD"))
144                .appendOptional(DateTimeFormat.forPattern("Z").getParser())
145                .toFormatter().withZoneUTC());
146                dateTimeParsers.put(XSD.G_DAY, new DateTimeFormatterBuilder()
147                .append(DateTimeFormat.forPattern("---DD"))
148                .appendOptional(DateTimeFormat.forPattern("Z").getParser())
149                .toFormatter().withZoneUTC());
150//              dateTimeParsers.put(XSD.TIME, DateTimeFormat.forPattern("hh:mm:ss.sss").withOffsetParsed());
151//              dateTimeParsers.put(XSD.DATE, ISODateTimeFormat.date());
152                dateTimeParsers.put(XSD.DATE_TIME, new DateTimeFormatterBuilder()
153                .append(DateTimeFormat.forPattern("yyyy-MM-DD'T'HH:mm:ss"))
154                .appendOptional(new DateTimeFormatterBuilder()
155                .appendLiteral('.')
156                .appendFractionOfSecond(1,4)
157                .toParser())
158                .appendOptional(DateTimeFormat.forPattern("Z").getParser())
159                .toFormatter().withZoneUTC());
160//              dateTimeParsers.put(XSD.DATE_TIME, ISODateTimeFormat.dateHourMinuteSecond()); //  .dateTimeNoMillis());
161//              dateTimeParsers.put(OWL2DatatypeImpl.getDatatype(OWL2Datatype.XSD_DATE_TIME_STAMP), ISODateTimeFormat.dateTimeNoMillis().withOffsetParsed());
162        }
163        
164        public static final Map<OWLDatatype, PeriodFormatter> periodFormatters = new HashMap<>();
165        static {
166                periodFormatters.put(XSD.DURATION, ISOPeriodFormat.standard());
167                periodFormatters.put(new OWLDatatypeImpl(IRI.create(org.apache.jena.vocabulary.XSD.getURI() + "dayTimeDuration")),
168                                new PeriodFormatterBuilder()//PnDTnHnMnS
169        .appendLiteral("P")
170        .appendDays()
171        .appendSuffix("D")
172        .appendSeparatorIfFieldsAfter("T")
173        .appendHours()
174        .appendSuffix("H")
175        .appendMinutes()
176        .appendSuffix("M")
177        .appendSecondsWithOptionalMillis()
178        .appendSuffix("S")
179        .toFormatter());
180                periodFormatters.put(new OWLDatatypeImpl(IRI.create(org.apache.jena.vocabulary.XSD.getURI() + "yearMonthDuration")),
181                                new PeriodFormatterBuilder()//PnYnM
182        .appendLiteral("P")
183        .appendYears()
184        .appendSuffix("Y")
185        .appendMonths()
186        .appendSuffix("M")
187        .toFormatter());
188                
189        }
190
191        /**
192         * @param entityType the OWL entity type
193         * @return the name of the OWL entity type
194         */
195        public static String getPrintName(EntityType entityType) {
196        return entityType.getPrintName().toLowerCase();
197        }
198
199        /**
200         * @param lit the OWL literal
201         * @return whether the OWL literal is an integer, i.e. whether the datatype is some integer
202         */
203        public static boolean isIntegerDatatype(OWLLiteral lit) {
204                return intDatatypes.contains(lit.getDatatype());
205        }
206        
207        public static boolean isIntegerDatatype(OWLDatatype datatype) {
208                return intDatatypes.contains(datatype);
209        }
210        
211        public static boolean isNumericDatatype(OWLDatatype datatype){
212                return numericDatatypes.contains(datatype);
213    }
214
215        /**
216         * Convenience method that converts a set of OWL class expressions to a set of OWL classes.
217         * @param classExpressions a set of OWL class expressions
218         * @return a set of OWL classes
219         */
220        public static Set<OWLClass> asOWLClasses(Set<OWLClassExpression> classExpressions) {
221                return Sets.newHashSet(Iterables.transform(classExpressions, OWL_CLASS_TRANSFORM_FUNCTION));
222        }
223
224        public static final String UNPARSED_OCE = "dllearner+unparsed:";
225
226        
227        public static OWLClassExpression classExpressionPropertyExpander (OWLClassExpression startClass, AbstractReasonerComponent reasoner, OWLDataFactory dataFactory, boolean sfp) {
228                if(!startClass.isAnonymous() && startClass.asOWLClass().getIRI().toString().startsWith(UNPARSED_OCE)) {
229                        try {
230                                String s = startClass.asOWLClass().getIRI().toString().substring(UNPARSED_OCE.length());
231                                return fromManchester(s, reasoner, dataFactory, sfp);
232                        } catch (ManchesterOWLSyntaxParserException e) {
233                                throw new RuntimeException("Parsing of class expression in OWL Manchester Syntax failed. Please check the syntax and "
234                                                + "remember to use either full IRIs or prefixed IRIs.", e);
235                        }
236                } else {
237                        return startClass;
238                }
239
240        }
241        public static OWLClassExpression classExpressionPropertyExpander (OWLClassExpression startClass, AbstractReasonerComponent reasoner, OWLDataFactory dataFactory) {
242                return classExpressionPropertyExpander(startClass, reasoner, dataFactory, false);
243        }
244
245        @NotNull
246        public static OWLClassExpression fromManchester(String expr, AbstractReasonerComponent reasoner, OWLDataFactory dataFactory) {
247                ManchesterOWLSyntaxClassExpressionParser parser = new ManchesterOWLSyntaxClassExpressionParser(dataFactory, new SimpleOWLEntityChecker(reasoner));
248                return parser.parse(expr);
249        }
250
251        @NotNull
252        public static OWLClassExpression fromManchester(String expr, AbstractReasonerComponent reasoner, OWLDataFactory dataFactory, boolean shortForm) {
253                SimpleOWLEntityChecker oec = new SimpleOWLEntityChecker(reasoner);
254                oec.setAllowShortForm(shortForm);
255                ManchesterOWLSyntaxClassExpressionParser parser = new ManchesterOWLSyntaxClassExpressionParser(dataFactory, oec);
256                return parser.parse(expr);
257        }
258
259        /**
260         * Checks whether the given value is in the closed interval [min,max], i.e.
261         * the value is greater than min and lower than max.
262         * @param value the value
263         * @param min the lower interval endpoint
264         * @param max the upper interval endpoint
265         * @return whether the given value is in the closed interval [min,max]
266         */
267        public static boolean inRange(OWLLiteral value, OWLLiteral min, OWLLiteral max) {
268                OWLDatatype datatype = value.getDatatype();
269                
270                if(OWLAPIUtils.dtDatatypes.contains(datatype)) {
271                        DateTimeFormatter parser = OWLAPIUtils.dateTimeParsers.get(datatype);
272                        
273                        // parse min
274                        DateTime minDateTime = null;
275                        if(min != null) {
276                                minDateTime = parser.parseDateTime(min.getLiteral());
277                        }
278                        
279                        // parse max
280                        DateTime maxDateTime = null;
281                        if(max != null) {
282                                maxDateTime = parser.parseDateTime(max.getLiteral());
283                        }
284                        
285                        // parse value
286                        DateTime dateTime = parser.parseDateTime(value.getLiteral());
287                        
288                        if(
289                                        (minDateTime == null || (dateTime.isEqual(minDateTime) ||  dateTime.isAfter(minDateTime)))
290                                        &&
291                                        (maxDateTime == null || (dateTime.isEqual(maxDateTime) ||  dateTime.isBefore(maxDateTime)))
292                                        )
293                                        {
294                                return true;
295                        }
296                } else if(OWLAPIUtils.floatDatatypes.contains(datatype)) {
297                        return Range.between(min.parseFloat(), max.parseFloat()).contains(value.parseFloat());
298                } else if(OWLAPIUtils.intDatatypes.contains(datatype)) {
299                        return Range.between(min.parseInteger(), max.parseInteger()).contains(value.parseInteger());
300                }
301                
302                return false;
303        }
304
305        public static OWLClassExpression classExpressionPropertyExpanderChecked(OWLClassExpression startClass, AbstractReasonerComponent reasoner, final OWLDataFactory df, Logger logger) {
306                return classExpressionPropertyExpanderChecked(startClass, reasoner, df, df::getOWLThing, logger);
307        }
308
309        public static OWLClassExpression classExpressionPropertyExpanderChecked(OWLClassExpression startClass, AbstractReasonerComponent reasoner, OWLDataFactory df, Supplier<OWLClassExpression> defaultClass, Logger logger, boolean sfp) {
310                if(startClass == null) {
311                        if (defaultClass != null)
312                                startClass = defaultClass.get();
313                } else {
314                        try {
315                                startClass = OWLAPIUtils.classExpressionPropertyExpander(startClass, reasoner, df, sfp);
316                        } catch (ManchesterOWLSyntaxParserException e) {
317                                logger.info("Error parsing startClass: " + e.getMessage());
318                                startClass = defaultClass.get();
319                                logger.warn("Using "+ startClass +" instead.");
320                        }
321                }
322                return startClass;
323        }
324        public static OWLClassExpression classExpressionPropertyExpanderChecked(OWLClassExpression startClass, AbstractReasonerComponent reasoner, OWLDataFactory df, Supplier<OWLClassExpression> defaultClass, Logger logger) {
325                return classExpressionPropertyExpanderChecked(startClass, reasoner, df, defaultClass, logger, false);
326        }
327        public static OWLClassExpression classExpressionPropertyExpanderChecked(OWLClassExpression startClass, AbstractReasonerComponent reasoner, OWLDataFactory df, boolean sfp, Logger logger) {
328                return classExpressionPropertyExpanderChecked(startClass, reasoner, df, null, logger, sfp);
329        }
330}