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
74
75 AbsObject abs = schema.newInstance();
76
77 String[] names = schema.getNames();
78
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
92 Method getMethod = findMethodCaseInsensitive(methodName, javaClass);
93 AbsObject value = invokeGetMethod(referenceOnto, getMethod, obj);
94
95
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
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
153
154 ObjectSchema schema = onto.getSchema(type, false);
155 if (schema == null) {
156 throw new UnknownSchemaException();
157 }
158
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
168
169 Object obj = javaClass.newInstance();
170
171
172 String[] names = schema.getNames();
173
174
175 for (int i = 0; i < names.length; ++i) {
176
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
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
222
223
224 if (objValue instanceof Long) {
225 Integer i = new Integer((int) ((Long) objValue).longValue());
226 params[0] = i;
227 }
228
229 else if (objValue instanceof Double) {
230 Float f = new Float((float) ((Double) objValue).doubleValue());
231 params[0] = f;
232 }
233
234 method.invoke(obj, params);
235 }
236 }
237 catch (OntologyException oe) {
238
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
264
265
266 if (objValue instanceof Long) {
267 Integer i = new Integer((int) ((Long) objValue).longValue());
268 params[0] = i;
269 }
270
271 else if (objValue instanceof Double) {
272 Float f = new Float((float) ((Double) objValue).doubleValue());
273 params[0] = f;
274 }
275
276 method.invoke(obj, params);
277 }
278 }
279 }
280 catch (OntologyException oe) {
281
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
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
322 break;
323 case '-':
324
325
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