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
289
290
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
312
313
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
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
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
399 return introspector.internalise(this, globalOnto, abs);
400 }
401 else {
402
403 throw new UnknownSchemaException();
404 }
405 }
406 catch (UnknownSchemaException use1) {
407
408 for (int i = 0; i < base.length; ++i) {
409 try {
410 return base[i].toObject(abs, globalOnto);
411 }
412 catch (UnknownSchemaException use2) {
413
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
434
435
436
437
438 try {
439 if (introspector != null) {
440
441 return introspector.externalise(this, globalOnto, obj);
442 }
443 else {
444 throw new UnknownSchemaException();
445 }
446 }
447 catch (UnknownSchemaException use1) {
448
449 for (int i = 0; i < base.length; ++i) {
450 try {
451 return base[i].fromObject(obj, globalOnto);
452 }
453 catch (UnknownSchemaException use2) {
454
455 }
456 }
457 throw use1;
458 }
459 }
460
461
462
463
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
478 obj instanceof Float ||
479 obj instanceof Double ||
480
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
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
545 throw new OntologyException("Type incompatibility: attribute "+attrName+" of "+abs+" is of type "+attrValue);
546 }
547
548
549 }