/******************************************************************************* * Copyright 2014 Miami-Dade County * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package org.sharegov.cirm; import static org.sharegov.cirm.OWL.allProperties; import static org.sharegov.cirm.OWL.annotate; import static org.sharegov.cirm.OWL.individual; import static org.sharegov.cirm.OWL.ontology; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import mjson.Json; import org.semanticweb.owlapi.model.OWLClass; import org.semanticweb.owlapi.model.OWLClassAssertionAxiom; import org.semanticweb.owlapi.model.OWLClassExpression; import org.semanticweb.owlapi.model.OWLDataProperty; import org.semanticweb.owlapi.model.OWLIndividual; import org.semanticweb.owlapi.model.OWLLiteral; import org.semanticweb.owlapi.model.OWLNamedIndividual; import org.semanticweb.owlapi.model.OWLNamedObject; import org.semanticweb.owlapi.model.OWLObject; import org.semanticweb.owlapi.model.OWLObjectProperty; import org.semanticweb.owlapi.model.OWLOntology; import org.semanticweb.owlapi.model.OWLProperty; import org.semanticweb.owlapi.model.OWLPropertyExpression; import org.semanticweb.owlapi.util.ShortFormProvider; import org.semanticweb.owlapi.vocab.OWL2Datatype; import org.sharegov.cirm.owl.OWLObjectPropertyCondition; import org.sharegov.cirm.owl.OWLProtectedClassCache; import org.sharegov.cirm.utils.GenUtils; import static org.sharegov.cirm.utils.GenUtils.*; /** * Converts OWL to JSON recursively. * Version 2 provides more comprehensive and thread safe use of a ShortFormProvider. * * @author Thomas Hilpold * */ public class OWLObjectToJson implements OWLObjectMapper<Json> { public final static boolean STRICT_TYPES = false; private boolean includeTypeInfo = false; private OWLObjectPropertyCondition stopExpansionCondition; //Must be thread safe private Set<String> includeTypeFor = new HashSet<String>(); public boolean isIncludeTypeInfo() { return includeTypeInfo; } public void setIncludeTypeInfo(boolean includeTypeInfo) { this.includeTypeInfo = includeTypeInfo; } public OWLObjectToJson() {} public OWLObjectToJson(OWLObjectPropertyCondition stopExpansionCondition) { if (stopExpansionCondition == null) throw new IllegalArgumentException(); this.stopExpansionCondition = stopExpansionCondition; } /** * If false only the iri and type information of an indivdual, which is a member of the Protected class will be serialized. * In this case, all properties and annotations of a protected individual and individuals reachable only through a protected individuals object properties will be omitted. * Default is true. * @return */ public Set<String> getPropertiesToTypeDecorate() { return includeTypeFor; } @Override public Json map(OWLOntology ontology, OWLObject object, ShortFormProvider shortFormProvider) { if (shortFormProvider == null) shortFormProvider = OWLObjectMapper.DEFAULT_SHORTFORM_PROVIDER; return toJSON(ontology, object, true, new HashSet<OWLObject>(), shortFormProvider); } @SuppressWarnings("unchecked") private OWLObjectMapper<Json> findObjectMapper(ShortFormProvider shortFormProvider, OWLOntology ontology, OWLNamedObject object, String mapPropertyName) { OWLNamedIndividual asIndividual = individual(object.getIRI()); OWLNamedIndividual mapper = OWL.objectProperty(asIndividual, mapPropertyName); if (mapper == null) return null; String classname = shortFormProvider.getShortForm(mapper); try { return (OWLObjectMapper<Json>)Class.forName(classname).newInstance(); } catch (Exception ex) { throw new RuntimeException(ex); } } private OWLPropertyMapper<Json> findPropertyMapper(OWLProperty prop) { OWLNamedIndividual asIndividual = individual(prop.getIRI()); OWLNamedIndividual mapper = OWL.objectProperty(asIndividual, Refs.hasJsonMapper); if (mapper == null) return null; String classname = mapper.getIRI().getFragment(); try { @SuppressWarnings("unchecked") OWLPropertyMapper<Json> M = (OWLPropertyMapper<Json>)Class.forName(classname).newInstance(); M.configure(OWL.toJSON(asIndividual)); return M; } catch (Exception ex) { throw new RuntimeException(ex); } } private Json toJSON(OWLOntology ontology, OWLProperty prop, OWLObject x, Set<OWLObject> done, ShortFormProvider shortFormProvider) { boolean expandIndividual = (prop instanceof OWLObjectProperty)? (stopExpansionCondition == null || !stopExpansionCondition.isMet(prop)) : true; Json value = toJSON(ontology, x, expandIndividual, done, shortFormProvider); if (includeTypeInfo && this.includeTypeFor.contains(shortFormProvider.getShortForm(prop))) { if (prop instanceof OWLObjectProperty) { OWLClass type = OWL.getPropertyType(ontology, (OWLObjectProperty)prop); value = Json.object("type", type.getIRI().toString(), "literal", value); } else { OWLLiteral l = (OWLLiteral)x; if (l.getDatatype().getIRI().equals(OWL2Datatype.XSD_DATE_TIME_STAMP.getIRI())) value = Json.make(GenUtils.formatDate(OWL.parseDate(l))); value = Json.object("type", l.getDatatype().getIRI().toString(), "literal", value); } } else if (prop instanceof OWLDataProperty) { OWLLiteral l = (OWLLiteral)x; if (l.getDatatype().getIRI().equals(OWL2Datatype.XSD_DATE_TIME_STAMP.getIRI())) value = Json.make(GenUtils.formatDate(OWL.parseDate(l))); } return value; } @SuppressWarnings("rawtypes") private Json toJSON(OWLOntology ontology, OWLObject object, boolean expandIfIndividual, Set<OWLObject> done, ShortFormProvider shortFormProvider) { if (object == null) { return Json.nil(); } else if(done.contains(object)) { if (object instanceof OWLNamedIndividual) return Json.make(((OWLIndividual) object).asOWLNamedIndividual().getIRI().toString()); else if (object instanceof OWLLiteral) return Json.make(((OWLLiteral)object).getLiteral()); } done.add(object); if (object instanceof OWLNamedIndividual) { OWLNamedIndividual ind = (OWLNamedIndividual)object; // 1) Check for a custom mapper to use for the individual Set<OWLClassExpression> classes = ind.getTypes(ontology.getImportsClosure()); for (OWLClassExpression expr : classes) { if (! (expr instanceof OWLClass)) continue; OWLClass type = (OWLClass)expr; OWLObjectMapper<Json> jsonMapper = findObjectMapper(shortFormProvider, ontology(), type, Refs.hasJsonMapper); if (jsonMapper != null) return jsonMapper.map(ontology, object, shortFormProvider); } Json result = Json.object(); // 2) Find All Classes of the individual the type property will have the first one. boolean isProtectedIndividual = false; //reasoner.getTypes(ind, true).getFlattened(); if (!classes.isEmpty()) { OWLProtectedClassCache protectedCache = Refs.protectedClassCache.resolve(); // 2013.01.08 hilpold added multiple type support w protection Iterator<OWLClassExpression> it = classes.iterator(); OWLClass firstClass = it.next().asOWLClass(); String classIriFragment = shortFormProvider.getShortForm(firstClass); result.set("type", classIriFragment); isProtectedIndividual = protectedCache.isProtectedClass(firstClass); if (it.hasNext()) { List<String> extendedClassesFragments = new LinkedList<String>(); while (it.hasNext()) { OWLClass curClass = it.next().asOWLClass(); extendedClassesFragments.add(shortFormProvider.getShortForm(curClass)); if (!isProtectedIndividual) isProtectedIndividual = protectedCache.isProtectedClass(curClass); } result.set("extendedTypes", Json.array(extendedClassesFragments)); { //TODO hilpold think about what types, main type, et.c. in ontology, prefixes... String msg = "Encountered object with " + classes.size() + " types. First set as Json >type< property: " + firstClass + " \r\n of individual " + ind; //for (OWLClassExpression classExp : classes) { //msg += ("\r\n Classes:" + classExp.asOWLClass().getIRI().getFragment()); //} if (STRICT_TYPES) throw new IllegalArgumentException(msg); else if (dbg()) System.err.println(this.getClass() + " " + msg + "\r\n Type " + shortFormProvider.getShortForm(firstClass) + "was added to JSON "); } } if (dbg()) System.out.println("Type: " + classIriFragment + " for " + ind); //3) Mark the Individual as protected, if it is. if (isProtectedIndividual) { result.set("transient$protected", Boolean.TRUE); if (dbg()) System.out.println("transient$protected: " + shortFormProvider.getShortForm(ind)); } } result.set("iri", ind.getIRI().toString()); // Add the individuals Annotations try { annotate(ind, result, shortFormProvider); } catch (Throwable t) { System.err.println("Failed to annotate " + ind.getIRI() + " " + t.toString()); t.printStackTrace(System.err); } // 3) All properties for the individual (recursion), if it's not protected and expandProtectedIndividuals false Json properties = Json.object(); Map<OWLPropertyExpression<?,?>, Set<OWLObject>> props = allProperties(ind, ontology, true, true); for (Map.Entry<OWLPropertyExpression<?,?>, Set<OWLObject>> e : props.entrySet()) { if (e.getKey() instanceof OWLProperty) { OWLProperty prop = (OWLProperty) e.getKey(); Json value; OWLPropertyMapper<Json> jsonMapper = findPropertyMapper(prop); if (jsonMapper != null) value = jsonMapper.map(ontology, ind, prop, e.getValue()); else { value = Json.array(); for (OWLObject x : e.getValue()) { if (expandIfIndividual || prop instanceof OWLDataProperty) { value.add(toJSON(ontology, prop, x, done, shortFormProvider)); } else { //iri only, but don't add to done set, it might be needed later; maybe do... //done.add(x); if (x instanceof OWLNamedIndividual) value.add(Json.make(((OWLIndividual) x).asOWLNamedIndividual().getIRI().toString())); } } if (value.asJsonList().size() == 1) value = value.at(0); } properties.set(shortFormProvider.getShortForm(prop), value); } } result.with(properties); if (!expandIfIndividual && dbg()) { System.out.println("Not expanding object properties' objects for individual: " + ind); } return result; } else if (object instanceof OWLClass) { OWLClass cl = (OWLClass)object; Json result = Json.object().set("iri", cl.getIRI().toString()); annotate(cl, result, shortFormProvider); return result; } else if (object instanceof OWLLiteral) { return Json.make(((OWLLiteral)object).getLiteral()); } else { return new DefaultToJSONMapper().map(ontology, object, shortFormProvider); } } }