1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs.util;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.apache.commons.vfs.FileSystemConfigBuilder;
22 import org.apache.commons.vfs.FileSystemException;
23 import org.apache.commons.vfs.FileSystemManager;
24 import org.apache.commons.vfs.FileSystemOptions;
25
26 import java.lang.reflect.Array;
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Method;
30 import java.lang.reflect.Modifier;
31 import java.util.ArrayList;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.TreeMap;
36
37 /***
38 * This class use reflection to set a configuration value using the fileSystemConfigBuilder
39 * associated the a scheme.<br><br>
40 * Example:<br>
41 * <pre>
42 * FileSystemOptions fso = new FileSystemOptions();
43 * DelegatingFileSystemOptionsBuilder delegate = new DelegatingFileSystemOptionsBuilder(VFS.getManager());
44 * delegate.setConfigString(fso, "sftp", "identities", "c:/tmp/test.ident");
45 * delegate.setConfigString(fso, "http", "proxyPort", "8080");
46 * delegate.setConfigClass(fso, "sftp", "userinfo", TrustEveryoneUserInfo.class);
47 * </pre>
48 *
49 * @author <a href="mailto:imario@apache.org">Mario Ivankovits</a>
50 * @version $Revision: 480428 $ $Date: 2006-11-29 07:15:24 +0100 (Mi, 29 Nov 2006) $
51 */
52 public class DelegatingFileSystemOptionsBuilder
53 {
54 private Log log = LogFactory.getLog(DelegatingFileSystemOptionsBuilder.class);
55
56 private final static Class[] STRING_PARAM = new Class[]{String.class};
57
58 private final FileSystemManager manager;
59
60 private final Map beanMethods = new TreeMap();
61
62 private final static Map primitiveToObject = new TreeMap();
63
64 static
65 {
66 primitiveToObject.put(Void.TYPE.getName(), Void.class);
67 primitiveToObject.put(Boolean.TYPE.getName(), Boolean.class);
68 primitiveToObject.put(Byte.TYPE.getName(), Byte.class);
69 primitiveToObject.put(Character.TYPE.getName(), Character.class);
70 primitiveToObject.put(Short.TYPE.getName(), Short.class);
71 primitiveToObject.put(Integer.TYPE.getName(), Integer.class);
72 primitiveToObject.put(Long.TYPE.getName(), Long.class);
73 primitiveToObject.put(Double.TYPE.getName(), Double.class);
74 primitiveToObject.put(Float.TYPE.getName(), Float.class);
75 }
76
77 private static class Context
78 {
79 private final FileSystemOptions fso;
80 private final String scheme;
81 private final String name;
82 private final Object[] values;
83
84 private List configSetters;
85 private FileSystemConfigBuilder fileSystemConfigBuilder;
86
87 private Context(final FileSystemOptions fso, final String scheme, final String name, final Object[] values)
88 {
89 this.fso = fso;
90 this.scheme = scheme;
91 this.name = name;
92 this.values = values;
93 }
94 }
95
96 /***
97 * Constructor.<br>
98 * Pass in your fileSystemManager instance.
99 *
100 * @param manager the manager to use to get the fileSystemConfigBuilder assocated to a scheme
101 */
102 public DelegatingFileSystemOptionsBuilder(final FileSystemManager manager)
103 {
104 this.manager = manager;
105 }
106
107 protected FileSystemManager getManager()
108 {
109 return manager;
110 }
111
112 /***
113 * Set a single string value.
114 *
115 * @param fso FileSystemOptions
116 * @param scheme scheme
117 * @param name name
118 * @param value value
119 */
120 public void setConfigString(final FileSystemOptions fso, final String scheme, final String name, final String value) throws FileSystemException
121 {
122 setConfigStrings(fso, scheme, name, new String[]{value});
123 }
124
125 /***
126 * Set an array of string value.
127 *
128 * @param fso FileSystemOptions
129 * @param scheme scheme
130 * @param name name
131 * @param values values
132 */
133 public void setConfigStrings(final FileSystemOptions fso, final String scheme, final String name, final String[] values) throws FileSystemException
134 {
135 Context ctx = new Context(fso, scheme, name, values);
136
137 setValues(ctx);
138 }
139
140 /***
141 * Set a single class value.<br>
142 * The class has to implement a no-args constructor, else the instantiation might fail.
143 *
144 * @param fso FileSystemOptions
145 * @param scheme scheme
146 * @param name name
147 * @param className className
148 */
149 public void setConfigClass(final FileSystemOptions fso, final String scheme, final String name, final Class className) throws FileSystemException, IllegalAccessException, InstantiationException
150 {
151 setConfigClasses(fso, scheme, name, new Class[]{className});
152 }
153
154 /***
155 * Set an array of class values.<br>
156 * The class has to implement a no-args constructor, else the instantiation might fail.
157 *
158 * @param fso FileSystemOptions
159 * @param scheme scheme
160 * @param name name
161 * @param classNames classNames
162 */
163 public void setConfigClasses(final FileSystemOptions fso, final String scheme, final String name, final Class[] classNames) throws FileSystemException, IllegalAccessException, InstantiationException
164 {
165 Object values[] = new Object[classNames.length];
166 for (int iterClassNames = 0; iterClassNames < values.length; iterClassNames++)
167 {
168 values[iterClassNames] = classNames[iterClassNames].newInstance();
169 }
170
171 Context ctx = new Context(fso, scheme, name, values);
172
173 setValues(ctx);
174 }
175
176 /***
177 * sets the values using the informations of the given context.<br>
178 */
179 private void setValues(Context ctx) throws FileSystemException
180 {
181
182 if (!fillConfigSetters(ctx))
183 {
184 throw new FileSystemException("vfs.provider/config-key-invalid.error", new String[]
185 {
186 ctx.scheme,
187 ctx.name
188 });
189 }
190
191
192 ctx.fileSystemConfigBuilder = getManager().getFileSystemConfigBuilder(ctx.scheme);
193
194
195 Iterator iterConfigSetters = ctx.configSetters.iterator();
196 while (iterConfigSetters.hasNext())
197 {
198 Method configSetter = (Method) iterConfigSetters.next();
199 if (convertValuesAndInvoke(configSetter, ctx))
200 {
201 return;
202 }
203 }
204
205 throw new FileSystemException("vfs.provider/config-value-invalid.error", new Object[]
206 {
207 ctx.scheme,
208 ctx.name,
209 ctx.values
210 });
211 }
212
213 /***
214 * tries to convert the value and pass it to the given method
215 */
216 private boolean convertValuesAndInvoke(final Method configSetter, final Context ctx) throws FileSystemException
217 {
218 Class parameters[] = configSetter.getParameterTypes();
219 if (parameters.length < 2)
220 {
221 return false;
222 }
223 if (!parameters[0].isAssignableFrom(FileSystemOptions.class))
224 {
225 return false;
226 }
227
228 Class valueParameter = parameters[1];
229 Class type;
230 if (valueParameter.isArray())
231 {
232 type = valueParameter.getComponentType();
233 }
234 else
235 {
236 if (ctx.values.length > 1)
237 {
238 return false;
239 }
240
241 type = valueParameter;
242 }
243
244 if (type.isPrimitive())
245 {
246 Class objectType = (Class) primitiveToObject.get(type.getName());
247 if (objectType == null)
248 {
249 log.warn(Messages.getString("vfs.provider/config-unexpected-primitive.error", type.getName()));
250 return false;
251 }
252 type = objectType;
253 }
254
255 Class valueClass = ctx.values[0].getClass();
256 if (type.isAssignableFrom(valueClass))
257 {
258
259 invokeSetter(valueParameter, ctx, configSetter, ctx.values);
260 return true;
261 }
262 if (valueClass != String.class)
263 {
264 log.warn(Messages.getString("vfs.provider/config-unexpected-value-class.error", new String[]
265 {
266 valueClass.getName(),
267 ctx.scheme,
268 ctx.name
269 }));
270 return false;
271 }
272
273 Object convertedValues = java.lang.reflect.Array.newInstance(type, ctx.values.length);
274
275 Constructor valueConstructor;
276 try
277 {
278 valueConstructor = type.getConstructor(STRING_PARAM);
279 }
280 catch (NoSuchMethodException e)
281 {
282 valueConstructor = null;
283 }
284 if (valueConstructor != null)
285 {
286
287 for (int iterValues = 0; iterValues < ctx.values.length; iterValues++)
288 {
289 try
290 {
291 Array.set(convertedValues, iterValues, valueConstructor.newInstance(new Object[]{ctx.values[iterValues]}));
292 }
293 catch (InstantiationException e)
294 {
295 throw new FileSystemException(e);
296 }
297 catch (IllegalAccessException e)
298 {
299 throw new FileSystemException(e);
300 }
301 catch (InvocationTargetException e)
302 {
303 throw new FileSystemException(e);
304 }
305 }
306
307 invokeSetter(valueParameter, ctx, configSetter, convertedValues);
308 return true;
309 }
310
311 Method valueFactory;
312 try
313 {
314 valueFactory = type.getMethod("valueOf", STRING_PARAM);
315 if (!Modifier.isStatic(valueFactory.getModifiers()))
316 {
317 valueFactory = null;
318 }
319 }
320 catch (NoSuchMethodException e)
321 {
322 valueFactory = null;
323 }
324
325 if (valueFactory != null)
326 {
327
328 for (int iterValues = 0; iterValues < ctx.values.length; iterValues++)
329 {
330 try
331 {
332 Array.set(convertedValues, iterValues, valueFactory.invoke(null, new Object[]{ctx.values[iterValues]}));
333 }
334 catch (IllegalAccessException e)
335 {
336 throw new FileSystemException(e);
337 }
338 catch (InvocationTargetException e)
339 {
340 throw new FileSystemException(e);
341 }
342 }
343
344 invokeSetter(valueParameter, ctx, configSetter, convertedValues);
345 return true;
346 }
347
348 return false;
349 }
350
351 /***
352 * invokes the method with the converted values
353 */
354 private void invokeSetter(Class valueParameter, final Context ctx, final Method configSetter, final Object values)
355 throws FileSystemException
356 {
357 Object[] args;
358 if (valueParameter.isArray())
359 {
360 args = new Object[]
361 {
362 ctx.fso,
363 values
364 };
365 }
366 else
367 {
368 args = new Object[]
369 {
370 ctx.fso,
371 Array.get(values, 0)
372 };
373 }
374 try
375 {
376 configSetter.invoke(ctx.fileSystemConfigBuilder, args);
377 }
378 catch (IllegalAccessException e)
379 {
380 throw new FileSystemException(e);
381 }
382 catch (InvocationTargetException e)
383 {
384 throw new FileSystemException(e);
385 }
386 }
387
388 /***
389 * fills all available set*() methods for the context-scheme into the context.
390 */
391 private boolean fillConfigSetters(final Context ctx)
392 throws FileSystemException
393 {
394 Map schemeMethods = getSchemeMethods(ctx.scheme);
395 List configSetters = (List) schemeMethods.get(ctx.name.toLowerCase());
396 if (configSetters == null)
397 {
398 return false;
399 }
400
401 ctx.configSetters = configSetters;
402 return true;
403 }
404
405 /***
406 * get (cached) list of set*() methods for the given scheme
407 */
408 private Map getSchemeMethods(final String scheme) throws FileSystemException
409 {
410 Map schemeMethods = (Map) beanMethods.get(scheme);
411 if (schemeMethods == null)
412 {
413 schemeMethods = createSchemeMethods(scheme);
414 beanMethods.put(scheme, schemeMethods);
415 }
416
417 return schemeMethods;
418 }
419
420 /***
421 * create the list of all set*() methods for the given scheme
422 */
423 private Map createSchemeMethods(String scheme) throws FileSystemException
424 {
425 final FileSystemConfigBuilder fscb = getManager().getFileSystemConfigBuilder(scheme);
426 if (fscb == null)
427 {
428 throw new FileSystemException("vfs.provider/no-config-builder.error", scheme);
429 }
430
431 Map schemeMethods = new TreeMap();
432
433 Method methods[] = fscb.getClass().getMethods();
434 for (int iterMethods = 0; iterMethods < methods.length; iterMethods++)
435 {
436 Method method = methods[iterMethods];
437 if (!Modifier.isPublic(method.getModifiers()))
438 {
439 continue;
440 }
441
442 String methodName = method.getName();
443 if (!methodName.startsWith("set"))
444 {
445
446 continue;
447 }
448
449 String key = methodName.substring(3).toLowerCase();
450
451 List configSetter = (List) schemeMethods.get(key);
452 if (configSetter == null)
453 {
454 configSetter = new ArrayList(2);
455 schemeMethods.put(key, configSetter);
456 }
457 configSetter.add(method);
458 }
459
460 return schemeMethods;
461 }
462 }