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 JADE_SL.*;
28  import JADE_SL.abs.*;
29  import JADE_SL.schema.*;
30  import java.util.ArrayList;
31  import java.util.List;
32  import java.util.Iterator;
33  import java.lang.reflect.*;
34  
35  /***
36   * Backward Compatible reflective introspector. This Introspector 
37   * uses Java Reflection to translate java object to/from abstract
38   * descriptors as the <code>ReflectiveIntrospector</code> does, but 
39   * it assumes the accessors methods for aggregate slots to be in the 
40   * "old JADE style" i.e.
41   * <i> For every aggregate <b>slot</b> named <code>XXX</code>,
42   * with elements of type <code>T</code>, the Java class must have 
43   * two accessible methods, with the following signature:</i>
44   * <ul>
45   *  	<li> <code>Iterator getAllXXX()</code>
46   *  	<li> <code>void addXXX(T t)</code>
47   * </ul>
48   * @author Giovanni Caire - TILAB
49   */
50  public class BCReflectiveIntrospector implements Introspector {
51  
52  	/***
53     * Translate an object of a class representing an element in an
54     * ontology into a proper abstract descriptor 
55     * @param onto The ontology that uses this Introspector.
56     * @param referenceOnto The reference ontology in the context of
57     * this translation i.e. the most extended ontology that extends 
58     * <code>onto</code> (directly or indirectly). 
59     * @param obj The Object to be translated
60     * @return The Abstract descriptor produced by the translation 
61     * @throws UnknownSchemaException If no schema for the object to be
62     * translated is defined in the ontology that uses this Introspector
63     * @throws OntologyException If some error occurs during the translation
64     */
65    public AbsObject externalise(Ontology onto, Ontology referenceOnto, Object obj) 
66      				throws UnknownSchemaException, OntologyException {
67    	try {
68        Class        javaClass = obj.getClass();            
69        ObjectSchema schema = onto.getSchema(javaClass);
70        if (schema == null) {
71          throw new UnknownSchemaException();
72        }
73      	//DEBUG 
74        //System.out.println("Schema is: "+schema);
75        AbsObject    abs = schema.newInstance();
76              
77        String[]     names = schema.getNames();
78        // Loop on slots
79        for (int i = 0; i < names.length; ++i) {
80        	String slotName = names[i];
81        	ObjectSchema slotSchema = schema.getSchema(slotName);
82        	
83        	String methodName;
84        	if (slotSchema instanceof AggregateSchema) {
85  					methodName = "getAll" + translateName(slotName);
86        	}
87        	else {
88  					methodName = "get" + translateName(slotName);
89        	}
90  
91        	// Retrieve the accessor method from the class and call it
92        	Method getMethod = findMethodCaseInsensitive(methodName, javaClass);
93          AbsObject value = invokeGetMethod(referenceOnto, getMethod, obj);
94          //DEBUG 
95          //System.out.println("Attribute value is: "+value);
96  
97          if (value != null) {
98            Ontology.setAttribute(abs, slotName, value);
99          } 
100       }
101     	return abs;
102   	}
103     catch (OntologyException oe) {
104     	throw oe;
105     } 
106     catch (Throwable t) {
107     	throw new OntologyException("Schema and Java class do not match", t);
108     } 
109   } 
110 
111   private AbsObject invokeGetMethod(Ontology onto, Method method, 
112                                       Object obj) throws OntologyException {
113     Object result = null;
114     try {
115       result = method.invoke(obj, null);
116 
117       if (result == null) {
118         return null;
119       } 
120 
121       return onto.fromObject(result);
122     } 
123     catch (OntologyException oe) {
124       // Forward the exception
125       throw oe;
126     } 
127     catch (Exception e) {
128     	throw new OntologyException("Error invoking get method", e);
129   	} 
130   } 
131 
132   /***
133    * Translate an abstract descriptor into an object of a proper class 
134    * representing an element in an ontology 
135    * @param onto The ontology that uses this Introspector.
136    * @param referenceOnto The reference ontology in the context of
137    * this translation i.e. the most extended ontology that extends 
138    * <code>onto</code> (directly or indirectly). 
139    * @param abs The abstract descriptor to be translated
140    * @return The Java object produced by the translation 
141    * @throws UngroundedException If the abstract descriptor to be translated 
142    * contains a variable
143    * @throws UnknownSchemaException If no schema for the abstract descriptor
144    * to be translated is defined in the ontology that uses this Introspector
145    * @throws OntologyException If some error occurs during the translation
146    */
147   public Object internalise(Ontology onto, Ontology referenceOnto, AbsObject abs) 
148             throws UngroundedException, UnknownSchemaException, OntologyException {
149 
150   	try {
151       String type = abs.getTypeName();
152       //DEBUG System.out.println("Abs is "+abs);
153       // Retrieve the schema
154       ObjectSchema schema = onto.getSchema(type, false);
155       if (schema == null) {
156         throw new UnknownSchemaException();
157       }
158       //DEBUG System.out.println("Schema is: "+schema);
159       if (schema instanceof IRESchema || schema instanceof VariableSchema) {
160         throw new UngroundedException();
161       }
162             
163       Class        javaClass = onto.getClassForElement(type);
164       if (javaClass == null) {
165         throw new OntologyException("No java class associated to type "+type);
166       }
167       //DEBUG System.out.println("Class is: "+javaClass.getName());
168             
169       Object       obj = javaClass.newInstance();
170       //DEBUG System.out.println("Object created");
171             
172       String[]     names = schema.getNames();
173 
174     	// LOOP on slots 
175     	for (int i = 0; i < names.length; ++i) {
176       	//SlotDescriptor desc = (SlotDescriptor)it.next();
177       	String slotName = names[i];
178       	AbsObject value = abs.getAbsObject(slotName);
179       	if (value != null) {
180 	      	ObjectSchema slotSchema = schema.getSchema(slotName);
181       	
182   	    	String methodName;
183     	  	if (slotSchema instanceof AggregateSchema) {
184 						methodName = "add" + translateName(slotName);
185       			Method addMethod = findMethodCaseInsensitive(methodName, javaClass);
186       			invokeAddMethod(referenceOnto, addMethod, obj, (AbsAggregate) value);
187       		}
188       		else {
189  						methodName = "set" + translateName(slotName);
190       			// Retrieve the modifier method from the class and call it
191       			Method setMethod = findMethodCaseInsensitive(methodName, javaClass);
192           	invokeSetMethod(referenceOnto, setMethod, obj, value);
193       		}
194         } 
195     	}
196       return obj;
197   	}
198     catch (OntologyException oe) {
199     	throw oe;
200     } 
201     catch (Throwable t) {
202     	throw new OntologyException("Schema and Java class do not match", t);
203     } 
204   } 
205 
206   private void invokeSetMethod(Ontology onto, Method method, Object obj, 
207                                  AbsObject value) throws OntologyException {
208   	try {
209       Object objValue = onto.toObject(value);
210 
211       if (objValue == null) {
212         return;
213       } 
214 
215       Object[] params = new Object[] {objValue};
216 						
217       try {
218 	      method.invoke(obj, params);
219       }
220       catch (IllegalArgumentException iae) {
221       	// Maybe the method required an int argument and we supplied 
222         // a Long. Similarly maybe the the method required a float and 
223         // we supplied a Double. Try these possibilities
224         if (objValue instanceof Long) {
225         	Integer i = new Integer((int) ((Long) objValue).longValue());
226         	params[0] = i;
227         }
228         //__CLDC_UNSUPPORTED__BEGIN
229         else if (objValue instanceof Double) {
230         	Float f = new Float((float) ((Double) objValue).doubleValue());
231         	params[0] = f;
232         }
233         //__CLDC_UNSUPPORTED__END
234         method.invoke(obj, params);
235       }
236     } 
237     catch (OntologyException oe) {
238       // Forward the exception
239       throw oe;
240     } 
241     catch (Exception e) {
242       throw new OntologyException("Error invoking set method", e);
243     } 
244   } 
245 
246   private void invokeAddMethod(Ontology onto, Method method, Object obj, 
247                                  AbsAggregate value) throws OntologyException {
248   	try {
249       List l = (List) onto.toObject(value);
250 
251       if (l == null) {
252         return;
253       } 
254 
255       Iterator it = l.iterator();
256       while (it.hasNext()) {
257       	Object objValue = it.next();
258 	      Object[] params = new Object[] {objValue};
259       	try {
260 	      	method.invoke(obj, params);
261       	}
262       	catch (IllegalArgumentException iae) {
263       		// Maybe the method required an Integer argument and we supplied 
264         	// a Long. Similarly maybe the the method required a Float and 
265         	// we supplied a Double. Try these possibilities
266         	if (objValue instanceof Long) {
267         		Integer i = new Integer((int) ((Long) objValue).longValue());
268         		params[0] = i;
269         	}
270         	//__CLDC_UNSUPPORTED__BEGIN
271         	else if (objValue instanceof Double) {
272         		Float f = new Float((float) ((Double) objValue).doubleValue());
273         		params[0] = f;
274         	}
275         	//__CLDC_UNSUPPORTED__END
276         	method.invoke(obj, params);
277       	}
278       }
279     } 
280     catch (OntologyException oe) {
281       // Forward the exception
282       throw oe;
283     } 
284     catch (Exception e) {
285       throw new OntologyException("Error invoking add method", e);
286     } 
287   } 
288 
289   /***
290      Check the structure of a java class associated to an ontological element 
291      to ensure that translations to/from abstract descriptors and java objects
292      (instances of that class) can be accomplished by this introspector.
293      @param schema The schema of the ontological element
294      @param javaClass The java class associated to the ontologcal element
295      @throws OntologyException if the java class does not have the correct 
296      structure
297    */
298   public void checkClass(ObjectSchema schema, Class javaClass) throws OntologyException {
299   	// FIXME: Not yet implemented
300   }
301     
302   private Method findMethodCaseInsensitive(String name, Class c) throws OntologyException {
303     Method[] methods = c.getMethods();
304     for(int i = 0; i < methods.length; i++) {
305       String ithName = methods[i].getName();
306       if(CaseInsensitiveString.equalsIgnoreCase(ithName, name))
307 				return methods[i];
308     }
309     throw new OntologyException("Method " + name + " not found in class "+c.getName());
310   }
311   
312 	private String translateName(String name) {
313 		StringBuffer buf = new StringBuffer();
314 
315 		boolean capitalize = false;
316 
317 		for (int i = 0; i < name.length(); i++) {
318 			char c = name.charAt(i);
319 			switch (c) {
320 			case ':':
321 				// Just ignore it
322 	     	break;
323 			case '-':
324 				// Don't copy the character, but capitalize the next
325 				// one so that x-y becomes xY
326 	     	capitalize = true;
327 				break;
328  			default:
329 				if (capitalize) {
330 					buf.append(Character.toUpperCase(c));
331 					capitalize = false;
332 				} 
333 				else {
334 					buf.append(c);
335 				} 
336 			}
337 		} 
338 		return buf.toString();
339 	} 
340 }
341