001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.vfs.util;
018:
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021: import org.apache.commons.vfs.FileSystemConfigBuilder;
022: import org.apache.commons.vfs.FileSystemException;
023: import org.apache.commons.vfs.FileSystemManager;
024: import org.apache.commons.vfs.FileSystemOptions;
025:
026: import java.lang.reflect.Array;
027: import java.lang.reflect.Constructor;
028: import java.lang.reflect.InvocationTargetException;
029: import java.lang.reflect.Method;
030: import java.lang.reflect.Modifier;
031: import java.util.ArrayList;
032: import java.util.Iterator;
033: import java.util.List;
034: import java.util.Map;
035: import java.util.TreeMap;
036:
037: /**
038: * This class use reflection to set a configuration value using the fileSystemConfigBuilder
039: * associated the a scheme.<br><br>
040: * Example:<br>
041: * <pre>
042: * FileSystemOptions fso = new FileSystemOptions();
043: * DelegatingFileSystemOptionsBuilder delegate = new DelegatingFileSystemOptionsBuilder(VFS.getManager());
044: * delegate.setConfigString(fso, "sftp", "identities", "c:/tmp/test.ident");
045: * delegate.setConfigString(fso, "http", "proxyPort", "8080");
046: * delegate.setConfigClass(fso, "sftp", "userinfo", TrustEveryoneUserInfo.class);
047: * </pre>
048: *
049: * @author <a href="mailto:imario@apache.org">Mario Ivankovits</a>
050: * @version $Revision: 480428 $ $Date: 2006-11-28 22:15:24 -0800 (Tue, 28 Nov 2006) $
051: */
052: public class DelegatingFileSystemOptionsBuilder {
053: private Log log = LogFactory
054: .getLog(DelegatingFileSystemOptionsBuilder.class);
055:
056: private final static Class[] STRING_PARAM = new Class[] { String.class };
057:
058: private final FileSystemManager manager;
059:
060: private final Map beanMethods = new TreeMap();
061:
062: private final static Map primitiveToObject = new TreeMap();
063:
064: static {
065: primitiveToObject.put(Void.TYPE.getName(), Void.class);
066: primitiveToObject.put(Boolean.TYPE.getName(), Boolean.class);
067: primitiveToObject.put(Byte.TYPE.getName(), Byte.class);
068: primitiveToObject
069: .put(Character.TYPE.getName(), Character.class);
070: primitiveToObject.put(Short.TYPE.getName(), Short.class);
071: primitiveToObject.put(Integer.TYPE.getName(), Integer.class);
072: primitiveToObject.put(Long.TYPE.getName(), Long.class);
073: primitiveToObject.put(Double.TYPE.getName(), Double.class);
074: primitiveToObject.put(Float.TYPE.getName(), Float.class);
075: }
076:
077: private static class Context {
078: private final FileSystemOptions fso;
079: private final String scheme;
080: private final String name;
081: private final Object[] values;
082:
083: private List configSetters;
084: private FileSystemConfigBuilder fileSystemConfigBuilder;
085:
086: private Context(final FileSystemOptions fso,
087: final String scheme, final String name,
088: final Object[] values) {
089: this .fso = fso;
090: this .scheme = scheme;
091: this .name = name;
092: this .values = values;
093: }
094: }
095:
096: /**
097: * Constructor.<br>
098: * Pass in your fileSystemManager instance.
099: *
100: * @param manager the manager to use to get the fileSystemConfigBuilder assocated to a scheme
101: */
102: public DelegatingFileSystemOptionsBuilder(
103: final FileSystemManager manager) {
104: this .manager = manager;
105: }
106:
107: protected FileSystemManager getManager() {
108: return manager;
109: }
110:
111: /**
112: * Set a single string value.
113: *
114: * @param fso FileSystemOptions
115: * @param scheme scheme
116: * @param name name
117: * @param value value
118: */
119: public void setConfigString(final FileSystemOptions fso,
120: final String scheme, final String name, final String value)
121: throws FileSystemException {
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,
134: final String scheme, final String name,
135: final String[] values) throws FileSystemException {
136: Context ctx = new Context(fso, scheme, name, values);
137:
138: setValues(ctx);
139: }
140:
141: /**
142: * Set a single class value.<br>
143: * The class has to implement a no-args constructor, else the instantiation might fail.
144: *
145: * @param fso FileSystemOptions
146: * @param scheme scheme
147: * @param name name
148: * @param className className
149: */
150: public void setConfigClass(final FileSystemOptions fso,
151: final String scheme, final String name,
152: final Class className) throws FileSystemException,
153: IllegalAccessException, InstantiationException {
154: setConfigClasses(fso, scheme, name, new Class[] { className });
155: }
156:
157: /**
158: * Set an array of class values.<br>
159: * The class has to implement a no-args constructor, else the instantiation might fail.
160: *
161: * @param fso FileSystemOptions
162: * @param scheme scheme
163: * @param name name
164: * @param classNames classNames
165: */
166: public void setConfigClasses(final FileSystemOptions fso,
167: final String scheme, final String name,
168: final Class[] classNames) throws FileSystemException,
169: IllegalAccessException, InstantiationException {
170: Object values[] = new Object[classNames.length];
171: for (int iterClassNames = 0; iterClassNames < values.length; iterClassNames++) {
172: values[iterClassNames] = classNames[iterClassNames]
173: .newInstance();
174: }
175:
176: Context ctx = new Context(fso, scheme, name, values);
177:
178: setValues(ctx);
179: }
180:
181: /**
182: * sets the values using the informations of the given context.<br>
183: */
184: private void setValues(Context ctx) throws FileSystemException {
185: // find all setter methods suitable for the given "name"
186: if (!fillConfigSetters(ctx)) {
187: throw new FileSystemException(
188: "vfs.provider/config-key-invalid.error",
189: new String[] { ctx.scheme, ctx.name });
190: }
191:
192: // get the fileSystemConfigBuilder
193: ctx.fileSystemConfigBuilder = getManager()
194: .getFileSystemConfigBuilder(ctx.scheme);
195:
196: // try to find a setter which could accept the value
197: Iterator iterConfigSetters = ctx.configSetters.iterator();
198: while (iterConfigSetters.hasNext()) {
199: Method configSetter = (Method) iterConfigSetters.next();
200: if (convertValuesAndInvoke(configSetter, ctx)) {
201: return;
202: }
203: }
204:
205: throw new FileSystemException(
206: "vfs.provider/config-value-invalid.error",
207: new Object[] { ctx.scheme, ctx.name, ctx.values });
208: }
209:
210: /**
211: * tries to convert the value and pass it to the given method
212: */
213: private boolean convertValuesAndInvoke(final Method configSetter,
214: final Context ctx) throws FileSystemException {
215: Class parameters[] = configSetter.getParameterTypes();
216: if (parameters.length < 2) {
217: return false;
218: }
219: if (!parameters[0].isAssignableFrom(FileSystemOptions.class)) {
220: return false;
221: }
222:
223: Class valueParameter = parameters[1];
224: Class type;
225: if (valueParameter.isArray()) {
226: type = valueParameter.getComponentType();
227: } else {
228: if (ctx.values.length > 1) {
229: return false;
230: }
231:
232: type = valueParameter;
233: }
234:
235: if (type.isPrimitive()) {
236: Class objectType = (Class) primitiveToObject.get(type
237: .getName());
238: if (objectType == null) {
239: log
240: .warn(Messages
241: .getString(
242: "vfs.provider/config-unexpected-primitive.error",
243: type.getName()));
244: return false;
245: }
246: type = objectType;
247: }
248:
249: Class valueClass = ctx.values[0].getClass();
250: if (type.isAssignableFrom(valueClass)) {
251: // can set value directly
252: invokeSetter(valueParameter, ctx, configSetter, ctx.values);
253: return true;
254: }
255: if (valueClass != String.class) {
256: log.warn(Messages.getString(
257: "vfs.provider/config-unexpected-value-class.error",
258: new String[] { valueClass.getName(), ctx.scheme,
259: ctx.name }));
260: return false;
261: }
262:
263: Object convertedValues = java.lang.reflect.Array.newInstance(
264: type, ctx.values.length);
265:
266: Constructor valueConstructor;
267: try {
268: valueConstructor = type.getConstructor(STRING_PARAM);
269: } catch (NoSuchMethodException e) {
270: valueConstructor = null;
271: }
272: if (valueConstructor != null) {
273: // can convert using constructor
274: for (int iterValues = 0; iterValues < ctx.values.length; iterValues++) {
275: try {
276: Array
277: .set(
278: convertedValues,
279: iterValues,
280: valueConstructor
281: .newInstance(new Object[] { ctx.values[iterValues] }));
282: } catch (InstantiationException e) {
283: throw new FileSystemException(e);
284: } catch (IllegalAccessException e) {
285: throw new FileSystemException(e);
286: } catch (InvocationTargetException e) {
287: throw new FileSystemException(e);
288: }
289: }
290:
291: invokeSetter(valueParameter, ctx, configSetter,
292: convertedValues);
293: return true;
294: }
295:
296: Method valueFactory;
297: try {
298: valueFactory = type.getMethod("valueOf", STRING_PARAM);
299: if (!Modifier.isStatic(valueFactory.getModifiers())) {
300: valueFactory = null;
301: }
302: } catch (NoSuchMethodException e) {
303: valueFactory = null;
304: }
305:
306: if (valueFactory != null) {
307: // can convert using factory method (valueOf)
308: for (int iterValues = 0; iterValues < ctx.values.length; iterValues++) {
309: try {
310: Array
311: .set(
312: convertedValues,
313: iterValues,
314: valueFactory
315: .invoke(
316: null,
317: new Object[] { ctx.values[iterValues] }));
318: } catch (IllegalAccessException e) {
319: throw new FileSystemException(e);
320: } catch (InvocationTargetException e) {
321: throw new FileSystemException(e);
322: }
323: }
324:
325: invokeSetter(valueParameter, ctx, configSetter,
326: convertedValues);
327: return true;
328: }
329:
330: return false;
331: }
332:
333: /**
334: * invokes the method with the converted values
335: */
336: private void invokeSetter(Class valueParameter, final Context ctx,
337: final Method configSetter, final Object values)
338: throws FileSystemException {
339: Object[] args;
340: if (valueParameter.isArray()) {
341: args = new Object[] { ctx.fso, values };
342: } else {
343: args = new Object[] { ctx.fso, Array.get(values, 0) };
344: }
345: try {
346: configSetter.invoke(ctx.fileSystemConfigBuilder, args);
347: } catch (IllegalAccessException e) {
348: throw new FileSystemException(e);
349: } catch (InvocationTargetException e) {
350: throw new FileSystemException(e);
351: }
352: }
353:
354: /**
355: * fills all available set*() methods for the context-scheme into the context.
356: */
357: private boolean fillConfigSetters(final Context ctx)
358: throws FileSystemException {
359: Map schemeMethods = getSchemeMethods(ctx.scheme);
360: List configSetters = (List) schemeMethods.get(ctx.name
361: .toLowerCase());
362: if (configSetters == null) {
363: return false;
364: }
365:
366: ctx.configSetters = configSetters;
367: return true;
368: }
369:
370: /**
371: * get (cached) list of set*() methods for the given scheme
372: */
373: private Map getSchemeMethods(final String scheme)
374: throws FileSystemException {
375: Map schemeMethods = (Map) beanMethods.get(scheme);
376: if (schemeMethods == null) {
377: schemeMethods = createSchemeMethods(scheme);
378: beanMethods.put(scheme, schemeMethods);
379: }
380:
381: return schemeMethods;
382: }
383:
384: /**
385: * create the list of all set*() methods for the given scheme
386: */
387: private Map createSchemeMethods(String scheme)
388: throws FileSystemException {
389: final FileSystemConfigBuilder fscb = getManager()
390: .getFileSystemConfigBuilder(scheme);
391: if (fscb == null) {
392: throw new FileSystemException(
393: "vfs.provider/no-config-builder.error", scheme);
394: }
395:
396: Map schemeMethods = new TreeMap();
397:
398: Method methods[] = fscb.getClass().getMethods();
399: for (int iterMethods = 0; iterMethods < methods.length; iterMethods++) {
400: Method method = methods[iterMethods];
401: if (!Modifier.isPublic(method.getModifiers())) {
402: continue;
403: }
404:
405: String methodName = method.getName();
406: if (!methodName.startsWith("set")) {
407: // not a setter
408: continue;
409: }
410:
411: String key = methodName.substring(3).toLowerCase();
412:
413: List configSetter = (List) schemeMethods.get(key);
414: if (configSetter == null) {
415: configSetter = new ArrayList(2);
416: schemeMethods.put(key, configSetter);
417: }
418: configSetter.add(method);
419: }
420:
421: return schemeMethods;
422: }
423: }
|