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.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      		// Add proper facets
145     		addFacet(name, new TypedAggregateFacet(elementsSchema));
146     		addFacet(name, new CardinalityFacet(cardMin, cardMax));
147     	}
148     	catch (OntologyException oe) {
149     		// Should never happen
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 					//DEBUG
179 					//System.out.println("Added facet "+f+" to slot "+slotName); 
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                 	// Do nothing. Maybe the slot is defined in another super-schema
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     		// Get slot descriptors of super schemas
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         // Get slot descriptors directly defined in this schema
290         for (Enumeration e = slots.keys(); e.hasMoreElements(); ) {
291             v.setElementAt(e.nextElement(), position);
292             position--;
293         	//v.addElement(e.nextElement());
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   		// Validate all the attributes in the abstract descriptor
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 			// DEBUG
338   		//System.out.println("Validating "+value+" as a value for slot "+slotName); 
339   		// NOTE: for performance reasons we don't want to scan the schema 
340   		// to check if slotValue is a valid slot and THEN to scan again
341   		// the schema to validate value. This is the reason for the 
342   		// boolean return value of this method
343   		boolean slotFound = false;
344   		
345   		// If the slot is defined in this schema --> check the value
346   		// against the schema of the slot. Otherwise let the super-schema
347   		// where the slot is defined validate the value
348   		SlotDescriptor dsc = (SlotDescriptor) slots.get(slotName);
349   		if (dsc != null) {
350 				// DEBUG
351   			//System.out.println("Slot "+slotName+" is defined in schema "+this); 
352   			if (value == null) {
353   				// Check optionality
354   				if (dsc.optionality == MANDATORY) {
355   					throw new OntologyException("Missing value for mandatory slot "+slotName);
356   				}
357   				// Don't need to check facets on a null value for an optional slot
358   				return true;
359   			}
360   			else {
361   				// - Get from the ontology the schema s that defines the type 
362   				// of the abstract descriptor value.
363   				// - Check if this schema is compatible with the schema for 
364   				// slot slotName
365   				// - Finally check value against s
366   				ObjectSchema s = onto.getSchema(value.getTypeName());
367   				//DEBUG 
368   				//System.out.println("Actual schema for "+value+" is "+s); 
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   				//DEBUG
376   				//System.out.println("Schema "+s+" for type "+value+" is compatible with schema "+dsc.schema+" for slot "+slotName); 
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   					// Don't need to check other super-schemas
388   					break;
389   				}
390   			}
391   		}
392   		
393   		if (slotFound) {
394   			// Check value against the facets (if any) defined for the  
395   			// slot in this schema
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   					//DEBUG
402   					//System.out.println("Checking facet "+f+" defined on slot "+slotName);
403   					f.validate(value, onto);
404   				}
405   			}
406   			else {
407   				//DEBUG
408   				//System.out.println("No facets for slot "+slotName);
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   		// The base schema for the ObjectSchema class descends only
452   		// from itself
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