001 package nl.tudelft.tbm.eeni.owlstructure.processor;
002
003 import com.hp.hpl.jena.ontology.*;
004 import com.hp.hpl.jena.rdf.model.Resource;
005 import nl.tudelft.tbm.eeni.owlstructure.utils.CollectionUtils;
006 import org.apache.commons.logging.Log;
007 import org.apache.commons.logging.LogFactory;
008
009 import java.util.Collection;
010 import java.util.HashSet;
011 import java.util.Iterator;
012
013 /**
014 * Simplify complex intersectionOf / unionOf property ranges (as created by a.o. Protege)
015 * to a simple flat list of named classes, and deletes the anonymous classes from the ontology.
016 * <p/>
017 * This is needed because Owl2Java chokes on complex property ranges;
018 * therefore maybe this should be integrated in Owl2Java?
019 */
020 public class PropertyRangeSimplifier implements IOntologyProcessor {
021
022 static Log log = LogFactory.getLog(FunctionalPropertyInferer.class);
023
024 /**
025 * Creates a new property range simplifier
026 *
027 * @see PropertyRangeSimplifier
028 */
029 public PropertyRangeSimplifier() {
030 }
031
032 /**
033 * Run the propery range inferer on all classes in the given ontology
034 *
035 * @param ontModel The ontology model to work on
036 */
037 @Override
038 public OntModel process(OntModel ontModel) {
039 // Loop over all properties
040 Collection<OntProperty> properties = ontModel.listAllOntProperties().toList();
041 for (OntProperty property : properties) {
042 // Find existing ranges
043 Collection<Resource> oldRanges = new HashSet<Resource>(property.listRange().toList());
044
045 // Simplify the range list to a flat list of named classes
046 Collection<Resource> newRanges = simplifyRange(ontModel, oldRanges);
047
048 // Remove existing ranges from property (retained ranges will be included in newRanges and thus re-added)
049 for (Resource range : oldRanges) {
050 property.removeRange(range);
051 }
052 // Add new ranges to property
053 for (Resource range : newRanges) {
054 property.addRange(range);
055 }
056
057 // Debug output
058 log.info("Property range simplification for property: " + property.getLocalName() + "\n"
059 + getLogMessage("retaining range(s)", CollectionUtils.intersectCollections(oldRanges, newRanges)) + "\n"
060 + getLogMessage("adding range(s)", CollectionUtils.subtractCollections(newRanges, oldRanges)) + "\n"
061 + getLogMessage("removing range(s)", CollectionUtils.subtractCollections(oldRanges, newRanges)));
062
063 }
064
065 return ontModel;
066 }
067
068 /**
069 * Given a certain property, list what datatypes (boolean, double, string, etc)
070 * or classes are used for the values that instances refer to using this property.
071 */
072 private Collection<Resource> simplifyRange(OntModel ontModel, Collection<Resource> oldRanges) {
073 HashSet<Resource> newRanges = new HashSet<Resource>();
074
075 /*
076 * If keepExistingRanges is enabled, start with current ranges already assigned in the property range
077 */
078 // Loop over all existing ranges
079 Iterator<Resource> rangeIterator = oldRanges.iterator();
080 while (rangeIterator.hasNext()) {
081 Resource range = rangeIterator.next();
082 // See whether it is a class or not
083 if (range.canAs(OntClass.class)) {
084 // It is a class, cast it
085 OntClass rangeClass = range.as(OntClass.class);
086
087 // Check whether it is an anonymous class
088 if (rangeClass.isAnon()) {
089 // Handle intersection class
090 if (rangeClass.isIntersectionClass()) {
091 // Cast to IntersectionClass
092 IntersectionClass intersectionClass = rangeClass.asIntersectionClass();
093 // Add operands to range
094 newRanges.addAll(simplifyRange(ontModel, new HashSet<Resource>(intersectionClass.listOperands().toSet())));
095 // Delete anonymous IntersectionClass from the ontology
096 intersectionClass.remove();
097 continue;
098 }
099 // Handle union class
100 if (rangeClass.isUnionClass()) {
101 // Cast to UnionClass
102 UnionClass unionClass = rangeClass.asUnionClass();
103 // Add operands to range
104 newRanges.addAll(simplifyRange(ontModel, new HashSet<Resource>(unionClass.listOperands().toSet())));
105 // Delete anonymous UnionClass from the ontology
106 unionClass.remove();
107 continue;
108 }
109 }
110 }
111
112 // It wasn't an anonymous boolean class, so just keep it
113 newRanges.add(range);
114 }
115
116 return newRanges;
117 }
118
119 /**
120 * Format a debug message containing a message and a list of resource localNames
121 */
122 private String getLogMessage(String message, Collection<? extends Resource> resources) {
123 String result = " - " + message + ": ";
124 if (resources.size() > 0) {
125 int counter = 0;
126 for (Resource resource : resources) {
127 result += (counter++ > 0 ? ", " : "") + resource.getLocalName();
128 }
129 } else {
130 result += "none";
131 }
132 return result;
133 }
134
135 }