1   /*
2    * PropertyImpl.java
3    *
4    * Copyright (c) 2002, The University of Sheffield.
5    *
6    * This file is part of GATE (see http://gate.ac.uk/), and is free
7    * software, licenced under the GNU Library General Public License,
8    * Version 2, June1991.
9    *
10   * A copy of this licence is included in the distribution in the file
11   * licence.html, and is also available at http://gate.ac.uk/gate/licence.html.
12   *
13   * Kalina Bontcheva 11/2003
14   *
15   *
16   *  $Id: PropertyImpl.java,v 1.11 2005/12/14 14:28:58 julien_nioche Exp $
17   */
18  package gate.creole.ontology;
19  
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.LinkedList;
23  import java.util.Set;
24  import com.ontotext.gate.ontology.OntologyImpl;
25  
26  /**
27   * This class provides implementations for methods common to all types of
28   * ontological properties.
29   */
30  public class PropertyImpl extends OntologyResourceImpl implements Property {
31    /**
32     * The set of domain restrictions (i.e. {@link OClass} objects} for this
33     * property. This is composed from the {@link #directDomain} plus all the
34     * domain restrictions from the super-properties. Once calculated this value
35     * is cached.
36     */
37    protected Set domain;
38    /**
39     * The set of domain restrictions (i.e. {@link OClass} objects} set as domain
40     * directly for this property.
41     */
42    protected Set directDomain;
43    /**
44     * The set of range restrictions (i.e. {@link OClass} or {@link Class}
45     * objects) for this property. This is composed from the {@link #directRange}
46     * plus all the range restrictions from the super-properties. Once calculated
47     * this value is cached.
48     */
49    protected Set range;
50    /**
51     * The set of range restrictions (i.e. {@link OClass} or {@link Class}
52     * objects) set as range directly for this property.
53     */
54    protected Set directRange;
55    protected Set samePropertiesSet;
56    protected Set superPropertiesSet;
57    protected Set subPropertiesSet;
58    protected Set superPropertiesTransitiveClosure;
59    protected Set subPropertiesTransitiveClosure;
60    protected boolean functional;
61    protected boolean inverseFunctional;
62  
63    /**
64     * Creates a property.
65     * 
66     * @param name
67     *          the name of the property
68     * @param domain
69     *          the ontology class representing the domain for this property.
70     * @param range
71     *          a set containing range restrictions. These can either be
72     *          {@link OClass} or {@link Class} objects depending on the types of
73     *          the values that are permitted.
74     * @param ontology
75     *          the ontology this property is defined in.
76     */
77    public PropertyImpl(String name, String comment, Set domain, Set range,
78            Ontology ontology) {
79      super(name, comment, ontology);
80      this.directDomain = new HashSet(domain);
81      this.domain = new HashSet(directDomain);
82      this.directRange = new HashSet(range);
83      this.range = new HashSet(directRange);
84      samePropertiesSet = new HashSet();
85      superPropertiesSet = new HashSet();
86      subPropertiesSet = new HashSet();
87      superPropertiesTransitiveClosure = new HashSet();
88      subPropertiesTransitiveClosure = new HashSet();
89      this.functional = false;
90      this.inverseFunctional = false;
91    }
92  
93    public PropertyImpl(String name, String comment, OClass aDomainClass,
94            Object aRangeType, Ontology ontology) {
95      this(name, comment, new HashSet(), new HashSet(), ontology);
96      if(aDomainClass != null) {
97        this.directDomain.add(aDomainClass);
98        this.domain.add(aDomainClass);
99      }
100     this.directRange.add(aRangeType);
101     this.range.add(aRangeType);
102   }
103 
104   public String getName() {
105     return name;
106   }
107 
108   public String getURI() {
109     return uri;
110   }
111 
112   public void setURI(String theURI) {
113     uri = theURI;
114   }
115 
116   public void setSamePropertyAs(Property theProperty) {
117     this.samePropertiesSet.add(theProperty);
118   }
119 
120   public Set getSamePropertyAs() {
121     if(this.samePropertiesSet.isEmpty()
122             && !this.getOntology().getPropertyDefinitions().contains(this)) {
123       Property propDefinition = this.getOntology().getPropertyDefinitionByName(
124               this.name);
125       if(propDefinition == null)
126         return this.samePropertiesSet;
127       else return propDefinition.getSamePropertyAs();
128     }
129     return this.samePropertiesSet;
130   }
131 
132   public void addSuperProperty(Property property) {
133     this.superPropertiesSet.add(property);
134     // add restrictions from super-property to the domain set
135     domain.addAll(property.getDomain());
136     OntologyImpl.reduceToMostSpecificClasses(domain);
137     // propagate the changes to sub properties
138     Iterator subPropIter = getSubProperties(TRANSITIVE_CLOSURE).iterator();
139     while(subPropIter.hasNext()) {
140       Property aSubProperty = (Property)subPropIter.next();
141       if(aSubProperty instanceof PropertyImpl) {
142         ((PropertyImpl)aSubProperty).recalculateDomain();
143         ((PropertyImpl)aSubProperty).recalculateRange();
144       }
145     }
146   }
147 
148   /**
149    * Notifies this property that it should recalculate the range set (because
150    * the range of a super-property has changed).
151    */
152   protected void recalculateDomain() {
153     domain.clear();
154     domain.addAll(directDomain);
155     Iterator superPropIter = getSuperProperties(TRANSITIVE_CLOSURE).iterator();
156     while(superPropIter.hasNext()) {
157       domain.addAll(((Property)superPropIter.next()).getDomain());
158     }
159     OntologyImpl.reduceToMostSpecificClasses(domain);
160   }
161 
162   /**
163    * Notifies this property that it should recalculate the range set (because
164    * the range of a super-property has changed).
165    */
166   protected void recalculateRange() {
167     range.clear();
168     range.addAll(directRange);
169     Iterator superPropIter = getSuperProperties(TRANSITIVE_CLOSURE).iterator();
170     while(superPropIter.hasNext()) {
171       range.addAll(((Property)superPropIter.next()).getRange());
172     }
173     OntologyImpl.reduceToMostSpecificClasses(range);
174   }
175 
176   public void removeSuperProperty(Property property) {
177     this.superPropertiesSet.remove(property);
178   }
179 
180   /**
181    * Add a SuperPropertyOf relation between the given property and this.
182    * 
183    * @param property
184    */
185   public void addSubProperty(Property property) {
186     this.subPropertiesSet.add(property);
187   }
188 
189   public void removeSubProperty(Property property) {
190     this.subPropertiesSet.remove(property);
191   }
192 
193   /**
194    * Returns the set of domain classes for this property. This is composed from
195    * the classes declared as domain restriction for this property plus all the
196    * domain restrictions from the super-properties.
197    */
198   public Set getDomain() {
199     return this.domain;
200   }
201 
202   /**
203    * Returns the set of range classes for this property. This is composed from
204    * the classes declared as range restriction for this property plus all the
205    * range restrictions from the super-properties.
206    */
207   public Set getRange() {
208     return this.range;
209   }
210 
211   /*
212    * (non-Javadoc)
213    * 
214    * @see gate.creole.ontology.Property#isFunctional()
215    */
216   public boolean isFunctional() {
217     return functional;
218   }
219 
220   /*
221    * (non-Javadoc)
222    * 
223    * @see gate.creole.ontology.Property#isInverseFunctional()
224    */
225   public boolean isInverseFunctional() {
226     return inverseFunctional;
227   }
228 
229   /*
230    * (non-Javadoc)
231    * 
232    * @see gate.creole.ontology.Property#setFunctional(boolean)
233    */
234   public void setFunctional(boolean functional) {
235     this.functional = functional;
236   }
237 
238   /*
239    * (non-Javadoc)
240    * 
241    * @see gate.creole.ontology.Property#setInverseFunctional(boolean)
242    */
243   public void setInverseFunctional(boolean inverseFunctional) {
244     this.inverseFunctional = inverseFunctional;
245   }
246 
247   /**
248    * Checks whether a provided value can be a domain value for this property. If
249    * the value is an {@link OInstance} then, in order to be a valid domain value
250    * it needs to be a member of <b>all</b> the classes defined as members of
251    * the domain of this property. The domain of this property is defined
252    * recursively based on its super-properties as well. For any other types of
253    * values it returns <tt>true</tt>.
254    * 
255    * @param instance
256    *          the instance to be checked.
257    * @return <tt>true</tt> if the provided instance can be a domain value for
258    *         this property.
259    */
260   public boolean isValidDomain(OntologyResource resource) {
261     if(resource instanceof OInstance) {
262       OInstance instance = (OInstance)resource;
263       Set domainClasses = new HashSet(getDomain());
264       boolean result = true;
265       // if there are no restrictions on the domain, then any domain is valid
266       if(domainClasses.isEmpty()) return true;
267       Iterator instanceClassIter = instance.getOClasses().iterator();
268       while(result && instanceClassIter.hasNext()) {
269         OClass anInstanceClass = (OClass)instanceClassIter.next();
270         // first do the simple test
271         if(!domainClasses.contains(anInstanceClass)) {
272           // the class is not directly contained in the domain,
273           // maybe one super class is?
274           Set superClasses = anInstanceClass
275                   .getSuperClasses(OntologyConstants.TRANSITIVE_CLOSURE);
276           Set intersection = new HashSet(superClasses);
277           intersection.retainAll(domainClasses);
278           if(intersection.isEmpty()) result = false;
279         }
280       }
281       return result;
282     } else return true;
283   }
284 
285   /**
286    * Checks whether a provided instance can be a range value for this property.
287    * For an instance to be a valid range value it needs to be a member of <b>all</b>
288    * the classes defined as members of the range of this property. The range of
289    * this property is defined recursively based on its super-properties as well.
290    * 
291    * @param instance
292    *          the instance to be checked.
293    * @return <tt>true</tt> if the provided instance can be a range value for
294    *         this property.
295    */
296   public boolean isValidRange(Object value) {
297     if(value instanceof OInstance) {
298       // implementation for ObjectProperties
299       OInstance instance = (OInstance)value;
300       Set rangeClasses = new HashSet(getRange());
301       boolean result = true;
302       Iterator instanceClassIter = instance.getOClasses().iterator();
303       while(result && instanceClassIter.hasNext()) {
304         OClass anInstanceClass = (OClass)instanceClassIter.next();
305         // first do the simple test
306         if(!rangeClasses.contains(anInstanceClass)) {
307           // the class is not directly contained in the range,
308           // maybe one super class is?
309           Set superClasses = anInstanceClass
310                   .getSuperClasses(OntologyConstants.TRANSITIVE_CLOSURE);
311           Set intersection = new HashSet(superClasses);
312           intersection.retainAll(rangeClasses);
313           if(intersection.isEmpty()) result = false;
314         }
315       }
316       return result;
317     } else if(value instanceof OntologyResource) {
318       // implementation for generic (a.k.a. RDF) properties
319       return true;
320     } else {
321       // implementation for DataType properties
322       Iterator rangIter = getRange().iterator();
323       while(rangIter.hasNext()) {
324         if(!((Class)rangIter.next()).isAssignableFrom(value.getClass()))
325           return false;
326       }
327       return true;
328     }
329   }
330 
331   public String toString() {
332     return getName();
333   }
334 
335   /**
336    * Gets the set of super-properties for this property.
337    * 
338    * @param {@link OntologyConstants#DIRECT_CLOSURE}
339    *          for direct super-properties only or
340    *          {@link OntologyConstants#TRANSITIVE_CLOSURE} for all the
341    *          super-properties.
342    * @return a set of {@link Property} values.
343    */
344   public Set getSuperProperties(byte closure) {
345     switch(closure){
346       case DIRECT_CLOSURE:
347         return superPropertiesSet;
348       case TRANSITIVE_CLOSURE:
349         if(superPropertiesTransitiveClosure.size() == 0
350                 || getOntology().isModified())
351           calculateSuperPropertiesClosure();
352         return superPropertiesTransitiveClosure;
353       default:
354         throw new IllegalArgumentException("Unknown closure type: " + closure);
355     }
356   }
357 
358   protected void calculateSuperPropertiesClosure() {
359     superPropertiesTransitiveClosure.clear();
360     LinkedList properties = new LinkedList(superPropertiesSet);
361     while(properties.size() > 0) {
362       Property aSuperProperty = (Property)properties.remove(0);
363       if(superPropertiesTransitiveClosure.add(aSuperProperty)) {
364         // this super property hasn't been seen before
365         properties.addAll(aSuperProperty.getSuperProperties(DIRECT_CLOSURE));
366       }
367     }
368   }
369 
370   /**
371    * Gets the set of sub-properties for this property.
372    * 
373    * @param {@link OntologyConstants#DIRECT_CLOSURE}
374    *          for direct sub-properties only or
375    *          {@link OntologyConstants#TRANSITIVE_CLOSURE} for all the
376    *          sub-properties.
377    * @return a set of {@link Property} values.
378    */
379   public Set getSubProperties(byte closure) {
380     switch(closure){
381       case DIRECT_CLOSURE:
382         return subPropertiesSet;
383       case TRANSITIVE_CLOSURE:
384         if(subPropertiesTransitiveClosure.isEmpty()
385                 || getOntology().isModified()) calculateSubPropertiesClosure();
386         return subPropertiesTransitiveClosure;
387       default:
388         throw new IllegalArgumentException("Unknown closure type: " + closure);
389     }
390   }
391 
392   protected void calculateSubPropertiesClosure() {
393     subPropertiesTransitiveClosure.clear();
394     LinkedList properties = new LinkedList(subPropertiesSet);
395     while(properties.size() > 0) {
396       Property aSuperProperty = (Property)properties.remove(0);
397       if(subPropertiesTransitiveClosure.add(aSuperProperty)) {
398         // this super property hasn't been seen before
399         properties.addAll(aSuperProperty.getSubProperties(DIRECT_CLOSURE));
400       }
401     }
402   }
403 }
404