View Javadoc

1   /***
2    * ***************************************************************
3    * JADE - Java Agent DEvelopment Framework is a framework to develop
4    * multi-agent systems in compliance with the FIPA specifications.
5    * Copyright (C) 2000 CSELT S.p.A.
6    * 
7    * GNU Lesser General Public License
8    * 
9    * This library is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU Lesser General Public
11   * License as published by the Free Software Foundation,
12   * version 2.1 of the License.
13   * 
14   * This library is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   * Lesser General Public License for more details.
18   * 
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this library; if not, write to the
21   * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22   * Boston, MA  02111-1307, USA.
23   * **************************************************************
24   */
25  package JADE_SL.onto;
26  
27  import java.util.Hashtable;
28  import java.util.Date;
29  
30  import JADE_SL.*;
31  import JADE_SL.abs.*;
32  import JADE_SL.schema.*;
33  import java.util.List;
34  import java.util.Iterator;
35  import JADE_SL.CaseInsensitiveString;
36  
37  /***
38   *  An application-specific ontology describes the elements that agents
39   * can use within content of messages. It defines a vocabulary and 
40   * relationships between the elements in such a vocabulary. 
41   * The relationships can be:
42   * <ul>
43   * <li>structural, e.g., the predicate <code>fatherOf</code> accepts two 
44   *     parameters, a father and a set of children;
45   * <li>semantic, e.g., a concept of class <code>Man</code> is also of class
46   *     <code>Person</code>.
47   * </ul> 
48   * Application-specific ontologies are implemented through objects 
49   * of class <code>Ontology</code>.<br>
50   * An ontology is characterized by:
51   * <ul>
52   * <li>one name;
53   * <li>one (or more) base ontology that it extends;
54   * <li>a set of <i>element schemas</i>.
55   * </ul>
56   * Element schemas are objects describing the structure of concepts, actions, 
57   * and predicates. that are allowed in messages. For example, 
58   * <code>People</code> ontology contains an element schema called 
59   * <code>Person</code>. This schema states that a <code>Person</code> is
60   * characterized by a <code>name</code> and by an <code>address</code>:
61   * <code>
62   * ConceptSchema personSchema = new ConceptSchema(PERSON);
63   * personSchema.addSlot(NAME,    stringSchema);
64   * personSchema.addSlot(ADDRESS, addressSchema, ObjectSchema.OPTIONAL);
65   * </code>
66   * where <code>PERSON<code>, <code>NAME</code> and <code>ADDRESS</code> are
67   * string constants. When you register your schema with the ontology, such
68   * constants become part of the vocabulary of the ontology.<br>
69   * Schemas that describe concepts support inheritance. You can define the
70   * concept <code>Man</code> as a refinement of the concept <code>Person</code>:
71   * <code>
72   * ConceptSchema manSchema = new ConceptSchema(MAN);
73   * manSchema.addSuperSchema(personSchema);
74   * </code>
75   * Each element schema can be associated with a Java class to map elements of
76   * the ontology that comply with a schema with Java objects of that class. The
77   * following is a class that might be associated with the <code>Person</code>
78   * schema:
79   * <code>
80   * public class Person extends Concept {
81   *       private String  name    = null;
82   *       private Address address =  null;
83   *
84   *       public void setName(String name) {
85   *               this.name = name;
86   *       }
87   *
88   *       public void setAddress(Address address) {
89   *               this.address = address;
90   *       }
91   *
92   *       public String getName() {
93   *               return name;
94   *       }
95   *
96   *       public Address getAddress() {
97   *               return address;
98   *       }
99   * }
100  * </code>
101  * When sending/receiving messages you can represent your content in terms of
102  * objects belonging to classes that the ontology associates with schemas.<br>
103  * As the previous example suggests, you cannot use objects of class
104  * <code>Person</code> when asking for the value of some attribute, e.g., when 
105  * asking for the value of <code>address</code>. Basically, the problem is that
106  * you cannot 'assign' a variable to an attribute of an object, i.e. 
107  * you cannot write something like: 
108  * <code>person.setName(new Variable("X"))</code>.<br>
109  * In order to solve this problem, you can describe your content in terms of
110  * <i>abstract descriptors</i>. An abstract descriptor is an
111  * object that reifies an element of the ontology.
112  * The following is the creation of an abstract
113  * descriptor for a concept of type <code>Man</code>:
114  * <code>
115  * AbsConcept absMan = new AbsConcept(MAN);
116  * absMan.setSlot(NAME,    "John");
117  * absMan.setSlot(ADDRESS, absAddress);
118  * </code>
119  * where <code>absAddress</code> is the abstract descriptor for John's 
120  * address:
121  * <code>
122  * AbsConcept absAddress = new AbsConcept(ADDRESS);
123  * absAddress.setSlot(CITY, "London");
124  * </code>
125  * Objects of class <code>Ontology</code> allows you to:
126  * <ul>
127  * <li>register schemas with associated (i) a mandatory term of the 
128  *     vocabulary e.g. <code>NAME</code> and (ii) an optional Java class, 
129  *     e.g. <code>Person</code>;
130  * <li>retrieve the registered information through various keys.
131  * </ul>
132  * The framework already provides two ontologies that you can use for building your
133  * application-specific ontologies:
134  * <ul>
135  * <li><code>BasicOntology</code>: that provides all basic elements, i.e., 
136  *     primitive data types, aggregate types, etc.
137  * <li><code>ACLOntology</code>: that extends the <code>BasicOntology</code> and
138  *     provides the elements corresponding to the FIPA ACL performatives
139  * </ul>
140  * Application-specific ontologies should be implemented extending one of them 
141 
142  * @see JADE_SL.Concept
143  * @see JADE_SL.abs.AbsConcept
144  * @see JADE_SL.schema.ConceptSchema
145  * @see JADE_SL.onto.ACLOntology
146  * @see JADE_SL.onto.BasicOntology
147  * @author Federico Bergenti - Universita` di Parma
148  * @author Giovanni Caire - TILAB
149  */
150 public class Ontology {
151 	  private static final String DEFAULT_INTROSPECTOR_CLASS = "JADE_SL.onto.ReflectiveIntrospector";
152     private Ontology[]   base = new Ontology[0];
153     private String       name = null;
154     private Introspector introspector = null;
155     
156     private Hashtable    elements = new Hashtable();
157     private Hashtable    classes  = new Hashtable();
158     private Hashtable    schemas  = new Hashtable();
159     
160 
161     /***
162      * Construct an Ontology object with a given <code>name</code> 
163      * that extends a given ontology.
164      * The <code>ReflectiveIntrospector</code> is used by default to
165      * convert between Java objects and abstract descriptors.
166      * @param name The identifier of the ontology.
167      * @param base The base ontology.
168      */
169     public Ontology(String name, Ontology base) {
170       this(name, base, null);
171       try {
172       	introspector = (Introspector) Class.forName(DEFAULT_INTROSPECTOR_CLASS).newInstance();
173       }
174       catch (Exception e) {
175       	throw new RuntimeException("Class "+DEFAULT_INTROSPECTOR_CLASS+"for default Introspector not found");
176       }
177     }
178 
179     /***
180      * Construct an Ontology object with a given <code>name</code> 
181      * that uses a given Introspector to
182      * convert between Java objects and abstract descriptors.
183      * @param name The identifier of the ontology.
184      * @param introspector The introspector.
185      */
186     public Ontology(String name, Introspector introspector) {
187       this(name, new Ontology[0], introspector);
188     }
189 
190     /***
191      * Construct an Ontology object with a given <code>name</code> 
192      * that extends a given ontology and that uses a given Introspector to
193      * convert between Java objects and abstract descriptors.
194      * @param name The identifier of the ontology.
195      * @param base The base ontology.
196      * @param introspector The introspector.
197      */
198     public Ontology(String name, Ontology base, Introspector introspector) {
199       this(name, (base != null ? new Ontology[]{base} : new Ontology[0]), introspector); 
200     }
201 
202     /***
203      * Construct an Ontology object with a given <code>name</code> 
204      * that extends a given set of ontologies and that uses a given Introspector to
205      * convert between Java objects and abstract descriptors.
206      * @param name The identifier of the ontology.
207      * @param base The base ontology.
208      * @param introspector The introspector.
209      */
210     public Ontology(String name, Ontology[] base, Introspector introspector) {
211         this.name = name;
212         this.introspector = introspector;
213         this.base = (base != null ? base : new Ontology[0]);
214     }
215 
216     /***
217      * Retrieves the name of this ontology.
218      * @return the name of this ontology.
219      */
220     public String getName() {
221         return name;
222     }
223 
224     /***
225      * Adds a schema to this ontology
226      * @param schema The schema to add
227      * @throws OntologyException
228      */
229     public void add(ObjectSchema schema) throws OntologyException {
230         add(schema, null);
231     } 
232 
233 
234     /***
235      * Adds a schema to the ontology and associates it to the class
236      * <code>javaClass</code>
237      * @param schema the schema.
238      * @param javaClass the concrete class.
239      * @throws OntologyException
240      */
241     public void add(ObjectSchema schema, Class javaClass) throws OntologyException {
242         if (schema.getTypeName() == null) {
243             throw new OntologyException("Invalid schema identifier");
244         } 
245         
246 	if (introspector != null)
247 	    introspector.checkClass(schema, javaClass);
248         
249         CaseInsensitiveString s = new CaseInsensitiveString(schema.getTypeName());
250         elements.put(s, schema);
251 
252         if (javaClass != null) {
253             classes.put(s, javaClass);
254             schemas.put(javaClass, schema);
255         } 
256     } 
257 
258     /***
259      * Retrieves the schema associated with <code>name</code>. The 
260      * search is extended to the base ontologies if the schema is not
261      * found.
262      * @param name the name of the schema in the vocabulary.
263      * @return the schema or <code>null</code> if the schema is not found.
264      * @throws OntologyException 
265      */
266     public ObjectSchema getSchema(String name) throws OntologyException {
267     	return getSchema(name, true);
268 		}		
269             
270     /***
271      * Converts an abstract descriptor to a Java object of the proper class.
272      * @param abs the abstract descriptor.
273      * @return the object
274      * @throws UngroundedException if the abstract descriptor contains a 
275      * variable
276      * @throws OntologyException if some mismatch with the schema is found
277      * @see #fromObject(Object)
278      */
279   	public Object toObject(AbsObject abs) throws OntologyException, UngroundedException {
280 			if (abs == null) {
281 				return null;
282 			}
283 			
284   		try {
285   			return toObject(abs, this);
286   		}
287       catch (UnknownSchemaException use) {
288       	// If we get this exception here, the schema is globally unknown 
289       	// (i.e. is unknown in the reference ontology and all its base
290       	// ontologies) --> throw a generic OntologyException
291       	throw new OntologyException("No schema found for type "+abs.getTypeName());
292       } 
293   	}
294   	
295     /***
296      * Converts a Java object into a proper abstract descriptor.
297      * @param obj the object
298      * @return the abstract descriptor.
299      * @throws OntologyException if some mismatch with the schema is found
300      * @see #toObject(AbsObject)
301      */
302     public AbsObject fromObject(Object obj) throws OntologyException {
303     	if (obj == null) {
304     		return null;
305     	}
306     	
307     	try {
308     		return fromObject(obj, this);
309     	}
310       catch (UnknownSchemaException use) {
311       	// If we get this exception here, the schema is globally unknown 
312       	// (i.e. is unknown in the reference ontology and all its base
313       	// ontologies) --> throw a generic OntologyException
314       	throw new OntologyException("No schema found for class "+obj.getClass().getName());
315       } 
316     }
317 
318     /***
319      * Retrieves the schema associated with <code>name</code>.
320      * @param name the name of the schema in the vocabulary.
321      * @param searchInBase If <code>true</code> the 
322      * search is extended to the base ontologies if the schema is not
323      * found.
324      * @return the schema.
325      * @throws OntologyException
326      */
327     ObjectSchema getSchema(String name, boolean searchInBase) throws OntologyException {
328         if (name == null) {
329             throw new OntologyException("Null schema identifier");
330         } 
331 
332         ObjectSchema ret = (ObjectSchema) elements.get(name.toLowerCase());
333 
334         if (ret == null) {
335         	//System.out.println("Schema for "+name+" not found in "+getName());
336           if (searchInBase) {
337             for (int i = 0; i < base.length; ++i) {
338             	try {
339             		if (base[i] == null)
340             			System.out.println("Base ontology # "+i+" for ontology "+getName()+" is null");
341                 ret = base[i].getSchema(name);
342                 if (ret != null) {
343                 	return ret;
344                 }
345               }
346               catch (OntologyException oe) {
347                 // Ignore and try next one
348               }
349             }
350           } 
351         } 
352 
353         return ret;
354     } 
355 
356     /***
357      * Retrieves the schema associated with <code>javaClass</code>
358      * The search is not extended to the base ontologies
359      * @param javaClass the Java class
360      * @return the schema
361      * @throws OntologyException
362      */
363     ObjectSchema getSchema(Class javaClass) throws OntologyException {
364         if (javaClass == null) {
365             throw new OntologyException("Null schema identifier");
366         } 
367         return (ObjectSchema) schemas.get(javaClass);
368     } 
369 
370     /***
371      * Retrieves the concrete class associated with <code>name</code> in
372      * the vocabulary. The search is not extended to the base ontologies
373      * @param name the name of the schema.
374      * @return the Java class.
375      * @throws OntologyException
376      */
377     Class getClassForElement(String name) throws OntologyException {
378         if (name == null) {
379             throw new OntologyException("Null schema identifier");
380         } 
381 
382         return (Class) classes.get(name.toLowerCase());
383     } 
384     
385     /***
386      * Converts an abstract descriptor to a Java object of the proper class.
387      * @param abs the abstract descriptor.
388      * @param globalOnto The ontology this ontology is part of (i.e. the 
389      * ontology that extends this ontology).
390      * @return the object
391      * @throws OntologyException if some mismatch with the schema is found
392      * @throws UngroundedException if the abstract descriptor contains a 
393      * variable
394      */ 
395     private Object toObject(AbsObject abs, Ontology globalOnto) throws UngroundedException, OntologyException {
396     		try {
397     			if (introspector != null) {
398         		//DEBUG System.out.println("Try to internalise "+abs+" through "+introspector);
399         		return introspector.internalise(this, globalOnto, abs);
400     			}
401     			else {
402     				// If the introspector is not set all schemas are unknown
403     				throw new UnknownSchemaException();
404     			}
405         }
406         catch (UnknownSchemaException use1) {
407         	// Try to convert the abstract descriptor using the base ontologies
408         	for (int i = 0; i < base.length; ++i) {
409         		try {
410         			return base[i].toObject(abs, globalOnto);
411         		}
412         		catch (UnknownSchemaException use2) {
413         			// Try the next one
414         		}	
415         	}
416         	throw use1;
417         }    		
418     } 
419 
420     /***
421      * Converts a Java object into a proper abstract descriptor.
422      * @param obj the object
423      * @param globalOnto The ontology this ontology is part of (i.e. the 
424      * ontology that extends this ontology).
425      * @return the abstract descriptor.
426 		 * @throws UnknownSchemaException If no schema for the object to be
427 		 * translated is defined in this ontology.
428      * @throws OntologyException if some mismatch with the schema is found
429      */
430     private AbsObject fromObject(Object obj, Ontology globalOnto) 
431     			throws UnknownSchemaException, OntologyException {
432     				
433         // If the object is already an abstract descriptor, just return it
434     		//if (obj instanceof AbsObject) {
435     		//	return (AbsObject) obj;
436     		//}
437     		
438     		try {
439     			if (introspector != null) {
440         		//DEBUG System.out.println("Try to externalise "+obj+" through "+introspector);
441         		return introspector.externalise(this, globalOnto, obj);
442     			}
443     			else {
444     				throw new UnknownSchemaException();
445     			}
446     		}
447         catch (UnknownSchemaException use1) {
448         	// Try to convert the object using the base ontologies
449         	for (int i = 0; i < base.length; ++i) {
450         		try {
451 	        		return base[i].fromObject(obj, globalOnto);
452         		}
453         		catch (UnknownSchemaException use2) {
454         			// Try the next one
455         		}
456         	}
457         	throw use1;
458         }
459     } 
460 
461     
462     /////////////////////////
463     // Utility static methods
464     /////////////////////////
465     
466     /***
467      * Check whether a given object is a valid term.
468      * If it is an Aggregate (i.e. a <code>List</code>) it also check
469      * the elements.
470      * @throws OntologyException if the given object is not a valid term
471      */
472     public static void checkIsTerm(Object obj) throws OntologyException {
473     	if (obj instanceof String ||
474     		  obj instanceof Boolean ||
475     		  obj instanceof Integer ||
476     		  obj instanceof Long ||
477     		  //__CLDC_UNSUPPORTED__BEGIN
478     		  obj instanceof Float ||
479     		  obj instanceof Double ||
480     		  //__CLDC_UNSUPPORTED__END
481     		  obj instanceof Date ||
482     		  obj instanceof Term) {
483     		return;
484     	}
485     	if (obj instanceof List) {
486     		Iterator it = ((List) obj).iterator();
487     		while (it.hasNext()) {
488     			checkIsTerm(it.next());
489     		}
490     		return;
491     	}
492     	
493     	// If we reach this point the object is not a term
494     	throw new OntologyException("Object "+obj+" is not a term");
495     }
496  
497     /***
498      * Set an attribute in an abstract descriptor performing all 
499      * necessary type checks.
500      * @throws OntologyException if a type mismatch is detected
501      */
502     public static void setAttribute(AbsObject abs, String attrName, AbsObject attrValue) throws OntologyException { 
503     	if (abs instanceof AbsAgentAction) {
504 				if (attrValue instanceof AbsTerm) {
505 					((AbsAgentAction) abs).set(attrName, (AbsTerm) attrValue);
506 					return;
507 				}
508 				if (attrValue instanceof AbsPredicate) {
509 					((AbsAgentAction) abs).set(attrName, (AbsPredicate) attrValue);
510 					return;
511 				}
512 			}
513     	if (abs instanceof AbsConcept) {
514 				if (attrValue instanceof AbsTerm) {
515 					((AbsConcept) abs).set(attrName, (AbsTerm) attrValue);
516 					return;
517 				}
518 			}
519 			else if (abs instanceof AbsPredicate) {
520 				((AbsPredicate) abs).set(attrName, attrValue);
521 				return;
522 			}
523 			else if (abs instanceof AbsIRE) {
524 				if (attrValue instanceof AbsVariable && CaseInsensitiveString.equalsIgnoreCase(attrName, IRESchema.VARIABLE)) {
525 					((AbsIRE) abs).setVariable((AbsVariable) attrValue);
526 					return;
527 				}
528 				else if (attrValue instanceof AbsPredicate && CaseInsensitiveString.equalsIgnoreCase(attrName, IRESchema.PROPOSITION)) {
529 					((AbsIRE) abs).setProposition((AbsPredicate) attrValue);
530 					return;
531 				}
532 			}
533 			else if (abs instanceof AbsVariable) {
534 				if (attrValue instanceof AbsPrimitive && CaseInsensitiveString.equalsIgnoreCase(attrName, VariableSchema.NAME)) {
535 					((AbsVariable) abs).setName(((AbsPrimitive) attrValue).getString());
536 					return;
537 				}
538 				else if (attrValue instanceof AbsPrimitive && CaseInsensitiveString.equalsIgnoreCase(attrName, VariableSchema.VALUE_TYPE)) {
539 					((AbsVariable) abs).setType(((AbsPrimitive) attrValue).getString());
540 					return;
541 				}
542 			}
543 									
544 			// If we reach this point there is a type incompatibility
545 			throw new OntologyException("Type incompatibility: attribute "+attrName+" of "+abs+" is of type "+attrValue); 
546     }
547 			
548     
549 }