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.schema;
26
27 import JADE_SL.onto.*;
28 import JADE_SL.abs.*;
29 import java.util.Hashtable;
30 import java.util.Vector;
31 import java.util.Enumeration;
32 import JADE_SL.schema.facets.*;
33
34 /***
35 * @author Federico Bergenti - Universita` di Parma
36 * @author Giovanni Caire - TILAB
37 */
38 public class ObjectSchema {
39 private class SlotDescriptor {
40 private String name = null;
41 private ObjectSchema schema = null;
42 private int optionality = 0;
43
44 /***
45 Construct a SlotDescriptor
46 */
47 private SlotDescriptor(String name, ObjectSchema schema,
48 int optionality) {
49 this.name = name;
50 this.schema = schema;
51 this.optionality = optionality;
52 }
53
54 }
55
56 /***
57 Canstant value indicating that a slot in a schema is mandatory,
58 i.e. its value must not be null
59 */
60 public static final int MANDATORY = 0;
61 /***
62 Canstant value indicating that a slot in a schema is optional,
63 i.e. its value can be null
64 */
65 public static final int OPTIONAL = 1;
66 /***
67 Canstant value indicating that a slot in a schema has an
68 infinite maximum cardinality
69 */
70 public static final int UNLIMITED = -1;
71
72 private Hashtable slots = new Hashtable();
73 private Vector superSchemas = new Vector();
74 private String typeName = null;
75 private Hashtable facets = new Hashtable();
76
77 public static final String BASE_NAME = "Object";
78 private static ObjectSchema baseSchema = new ObjectSchema();
79
80 /***
81 * Construct a schema that vinculates an entity to be a generic
82 * object (i.e. no constraints at all)
83 */
84 private ObjectSchema() {
85 this(BASE_NAME);
86 }
87
88 /***
89 * Creates an <code>ObjectSchema</code> with a given type-name.
90 * @param typeName The name of this <code>ObjectSchema</code>.
91 */
92 protected ObjectSchema(String typeName) {
93 this.typeName = typeName;
94 }
95
96 /***
97 * Retrieve the generic base schema for all objects.
98 * @return the generic base schema for all objects.
99 */
100 public static ObjectSchema getBaseSchema() {
101 return baseSchema;
102 }
103
104 /***
105 * Add a slot to the schema.
106 * @param name The name of the slot.
107 * @param slotSchema The schema defining the type of the slot.
108 * @param optionality The optionality, i.e., <code>OPTIONAL</code>
109 * or <code>MANDATORY</code>
110 */
111 protected void add(String name, ObjectSchema slotSchema, int optionality) {
112 slots.put(name.toUpperCase(), new SlotDescriptor(name, slotSchema, optionality));
113 }
114
115 /***
116 * Add a mandatory slot to the schema.
117 * @param name name of the slot.
118 * @param slotSchema schema of the slot.
119 */
120 protected void add(String name, ObjectSchema slotSchema) {
121 add(name, slotSchema, MANDATORY);
122 }
123
124 /***
125 * Add a slot with cardinality between <code>cardMin</code>
126 * and <code>cardMax</code> to this schema.
127 * Adding such a slot is equivalent to add a slot
128 * of type Aggregate and then to add proper facets (constraints)
129 * to check that the type of the elements in the aggregate are
130 * compatible with <code>elementsSchema</code> and that the
131 * aggregate contains at least <code>cardMin</code> elements and
132 * at most <code>cardMax</code> elements.
133 * @param name The name of the slot.
134 * @param elementsSchema The schema for the elements of this slot.
135 * @param cardMin This slot must get at least <code>cardMin</code>
136 * values
137 * @param cardMax This slot can get at most <code>cardMax</code>
138 * values
139 */
140 protected void add(String name, ObjectSchema elementsSchema, int cardMin, int cardMax) {
141 int optionality = (cardMin == 0 ? OPTIONAL : MANDATORY);
142 try {
143 add(name, BasicOntology.getInstance().getSchema(BasicOntology.SEQUENCE), optionality);
144
145 addFacet(name, new TypedAggregateFacet(elementsSchema));
146 addFacet(name, new CardinalityFacet(cardMin, cardMax));
147 }
148 catch (OntologyException oe) {
149
150 oe.printStackTrace();
151 }
152 }
153
154 /***
155 * Add a super schema tho this schema, i.e. this schema will
156 * inherit all characteristics from the super schema
157 * @param superSchema the super schema.
158 */
159 protected void addSuperSchema(ObjectSchema superSchema) {
160 superSchemas.addElement(superSchema);
161 }
162
163 /***
164 Add a <code>Facet</code> on a slot of this schema
165 @param slotName the name of the slot the <code>Facet</code>
166 must be added to.
167 @param f the <code>Facet</code> to be added.
168 @throws OntologyException if slotName does not identify
169 a valid slot in this schema
170 */
171 protected void addFacet(String slotName, Facet f) throws OntologyException {
172 slotName = slotName.toUpperCase();
173 if (containsSlot(slotName)) {
174 Vector v = (Vector) facets.get(slotName);
175 if (v == null) {
176 v = new Vector();
177 facets.put(slotName, v);
178
179
180 }
181 v.addElement(f);
182 }
183 else {
184 throw new OntologyException(slotName+" is not a valid slot in this schema");
185 }
186 }
187
188 /***
189 * Retrieves the name of the type of this schema.
190 * @return the name of the type of this schema.
191 */
192 public String getTypeName() {
193 return typeName;
194 }
195
196 /***
197 * Returns the names of all the slots in this <code>Schema</code>
198 * (including slots defined in super schemas).
199 *
200 * @return the names of all slots.
201 */
202 public String[] getNames() {
203 Vector allSlotDescriptors = new Vector();
204
205 getAllSlotDescriptors(allSlotDescriptors);
206
207 String[] names = new String[allSlotDescriptors.size()];
208 int counter = 0;
209 for (Enumeration e = allSlotDescriptors.elements(); e.hasMoreElements(); ) {
210 names[counter++] = (String) e.nextElement();
211 }
212
213 return names;
214 }
215
216 /***
217 * Retrieves the schema of a slot of this <code>Schema</code>.
218 *
219 * @param name The name of the slot.
220 * @return the <code>Schema</code> of slot <code>name</code>
221 * @throws OntologyException If no slot with this name is present
222 * in this schema.
223 */
224 public ObjectSchema getSchema(String name) throws OntologyException {
225 name = name.toUpperCase();
226 SlotDescriptor slot = (SlotDescriptor) slots.get(name);
227
228 if (slot == null) {
229 for (Enumeration e = superSchemas.elements(); e.hasMoreElements(); ) {
230 try {
231 ObjectSchema superSchema = (ObjectSchema) e.nextElement();
232 return superSchema.getSchema(name);
233 }
234 catch (OntologyException oe) {
235
236 }
237 }
238
239 throw new OntologyException("No slot named: " + name);
240 }
241
242 return slot.schema;
243 }
244
245 /***
246 * Indicate whether a given <code>String</code> is the name of a
247 * slot defined in this <code>Schema</code>
248 *
249 * @param name The <code>String</code> to test.
250 * @return <code>true</code> if <code>name</code> is the name of a
251 * slot defined in this <code>Schema</code>.
252 */
253 public boolean containsSlot(String name) {
254 name = name.toUpperCase();
255 SlotDescriptor slot = (SlotDescriptor) slots.get(name);
256
257 if (slot != null) {
258 return true;
259 }
260
261 for (Enumeration e = superSchemas.elements(); e.hasMoreElements(); ) {
262 ObjectSchema superSchema = (ObjectSchema) e.nextElement();
263 if (superSchema.containsSlot(name)) {
264 return true;
265 }
266 }
267
268 return false;
269 }
270
271 /***
272 * Creates an Abstract descriptor to hold an object compliant to
273 * this <code>Schema</code>.
274 */
275 public AbsObject newInstance() throws OntologyException {
276 throw new OntologyException("AbsObject cannot be instantiated");
277 }
278
279 private void getAllSlotDescriptors(Vector v) {
280
281 for (Enumeration e = superSchemas.elements(); e.hasMoreElements(); ) {
282 ObjectSchema superSchema = (ObjectSchema) e.nextElement();
283
284 superSchema.getAllSlotDescriptors(v);
285 }
286
287 int position = v.size() + slots.size() - 1;
288 v.setSize(v.size() + slots.size());
289
290 for (Enumeration e = slots.keys(); e.hasMoreElements(); ) {
291 v.setElementAt(e.nextElement(), position);
292 position--;
293
294 }
295 }
296
297 /***
298 Check whether a given abstract descriptor complies with this
299 schema.
300 @param abs The abstract descriptor to be checked
301 @throws OntologyException If the abstract descriptor does not
302 complies with this schema
303 */
304 public void validate(AbsObject abs, Ontology onto) throws OntologyException {
305 validateSlots(abs, onto);
306 }
307
308 /***
309 For each slot
310 - get the corresponding attribute value from the abstract descriptor
311 abs
312 - Check that it is not null if the slot is mandatory
313 - Check that its schema is compatible with the schema of the slot
314 - Check that it is a correct abstract descriptor by validating it
315 against its schema.
316 */
317 protected void validateSlots(AbsObject abs, Ontology onto) throws OntologyException {
318
319 String[] slotNames = getNames();
320 for (int i = 0; i < slotNames.length; ++i) {
321 AbsObject slotValue = abs.getAbsObject(slotNames[i]);
322 validate(slotNames[i], slotValue, onto);
323 }
324 }
325
326 /***
327 Validate a given abstract descriptor as a value for a slot
328 defined in this schema
329 @param slotName The name of the slot
330 @param value The abstract descriptor to be validated
331 @throws OntologyException If the abstract descriptor is not a
332 valid value
333 @return true if the slot is defined in this schema (or in
334 one of its super schemas). false otherwise
335 */
336 private boolean validate(String slotName, AbsObject value, Ontology onto) throws OntologyException {
337
338
339
340
341
342
343 boolean slotFound = false;
344
345
346
347
348 SlotDescriptor dsc = (SlotDescriptor) slots.get(slotName);
349 if (dsc != null) {
350
351
352 if (value == null) {
353
354 if (dsc.optionality == MANDATORY) {
355 throw new OntologyException("Missing value for mandatory slot "+slotName);
356 }
357
358 return true;
359 }
360 else {
361
362
363
364
365
366 ObjectSchema s = onto.getSchema(value.getTypeName());
367
368
369 if (s == null) {
370 throw new OntologyException("No schema found for type "+value.getTypeName());
371 }
372 if (!s.isCompatibleWith(dsc.schema)) {
373 throw new OntologyException("Schema "+s+" for element "+value+" is not compatible with schema "+dsc.schema+" for slot "+slotName);
374 }
375
376
377 s.validate(value, onto);
378 }
379 slotFound = true;
380 }
381 else {
382 Enumeration e = superSchemas.elements();
383 while (e.hasMoreElements()) {
384 ObjectSchema s = (ObjectSchema) e.nextElement();
385 if (s.validate(slotName, value, onto)) {
386 slotFound = true;
387
388 break;
389 }
390 }
391 }
392
393 if (slotFound) {
394
395
396 Vector ff = (Vector) facets.get(slotName);
397 if (ff != null) {
398 Enumeration e = ff.elements();
399 while (e.hasMoreElements()) {
400 Facet f = (Facet) e.nextElement();
401
402
403 f.validate(value, onto);
404 }
405 }
406 else {
407
408
409 }
410 }
411
412 return slotFound;
413 }
414
415 /***
416 Check if this schema is compatible with a given schema s.
417 This is the case if
418 1) This schema is equals to s
419 2) s is one of the super-schemas of this schema
420 3) This schema descends from s i.e.
421 - s is the base schema for the XXXSchema class this schema is
422 an instance of (e.g. s is ConceptSchema.getBaseSchema() and this
423 schema is an instance of ConceptSchema)
424 - s is the base schema for a super-class of the XXXSchema class
425 this schema is an instance of (e.g. s is TermSchema.getBaseSchema()
426 and this schema is an instance of ConceptSchema)
427 */
428 public boolean isCompatibleWith(ObjectSchema s) {
429 if (equals(s)) {
430 return true;
431 }
432 if (isSubSchemaOf(s)) {
433 return true;
434 }
435 if (descendsFrom(s)) {
436 return true;
437 }
438 return false;
439 }
440
441 /***
442 Return true if
443 - s is the base schema for the XXXSchema class this schema is
444 an instance of (e.g. s is ConceptSchema.getBaseSchema() and this
445 schema is an instance of ConceptSchema)
446 - s is the base schema for a super-class of the XXXSchema class
447 this schema is an instance of (e.g. s is TermSchema.getBaseSchema()
448 and this schema is an instance of ConceptSchema)
449 */
450 protected boolean descendsFrom(ObjectSchema s) {
451
452
453 if (s!= null) {
454 return s.equals(getBaseSchema());
455 }
456 else {
457 return false;
458 }
459 }
460
461 /***
462 Return true if s is a super-schema (directly or indirectly)
463 of this schema
464 */
465 private boolean isSubSchemaOf(ObjectSchema s) {
466 Enumeration e = superSchemas.elements();
467 while (e.hasMoreElements()) {
468 ObjectSchema s1 = (ObjectSchema) e.nextElement();
469 if (s1.equals(s)) {
470 return true;
471 }
472 if (s1.isSubSchemaOf(s)) {
473 return true;
474 }
475 }
476 return false;
477 }
478
479 public String toString() {
480 return getClass().getName()+"-"+getTypeName();
481 }
482
483 public boolean equals(Object o) {
484 if (o != null) {
485 return toString().equals(o.toString());
486 }
487 else {
488 return false;
489 }
490 }
491 }
492