KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > directwebremoting > dwrp > DefaultConverterManager


1 /*
2  * Copyright 2005 Joe Walker
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.directwebremoting.dwrp;
17
18 import java.util.Arrays JavaDoc;
19 import java.util.Collection JavaDoc;
20 import java.util.Collections JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25
26 import org.directwebremoting.extend.Converter;
27 import org.directwebremoting.extend.ConverterManager;
28 import org.directwebremoting.extend.InboundContext;
29 import org.directwebremoting.extend.InboundVariable;
30 import org.directwebremoting.extend.MarshallException;
31 import org.directwebremoting.extend.NamedConverter;
32 import org.directwebremoting.extend.OutboundContext;
33 import org.directwebremoting.extend.OutboundVariable;
34 import org.directwebremoting.extend.TypeHintContext;
35 import org.directwebremoting.util.LocalUtil;
36 import org.directwebremoting.util.Logger;
37 import org.directwebremoting.util.Messages;
38
39 /**
40  * A class to manage the converter types and the instansiated class name matches.
41  * @author Joe Walker [joe at getahead dot ltd dot uk]
42  */

43 public class DefaultConverterManager implements ConverterManager
44 {
45     /* (non-Javadoc)
46      * @see org.directwebremoting.ConverterManager#addConverterType(java.lang.String, java.lang.String)
47      */

48     public void addConverterType(String JavaDoc id, String JavaDoc className)
49     {
50         if (!LocalUtil.isJavaIdentifier(id))
51         {
52             log.error("Illegal identifier: '" + id + "'");
53             return;
54         }
55
56         Class JavaDoc clazz = LocalUtil.classForName(id, className, Converter.class);
57         if (clazz != null)
58         {
59             log.debug("- adding converter type: " + id + " = " + clazz.getName());
60             converterTypes.put(id, clazz);
61         }
62     }
63
64     /* (non-Javadoc)
65      * @see org.directwebremoting.ConverterManager#addConverter(java.lang.String, java.lang.String, java.util.Map)
66      */

67     public void addConverter(String JavaDoc match, String JavaDoc type, Map JavaDoc params) throws IllegalArgumentException JavaDoc, InstantiationException JavaDoc, IllegalAccessException JavaDoc
68     {
69         Class JavaDoc clazz = (Class JavaDoc) converterTypes.get(type);
70         if (clazz == null)
71         {
72             log.info("Probably not an issue: " + match + " is not available so the " + type + " converter will not load. This is only an problem if you wanted to use it.");
73             return;
74         }
75
76         Converter converter = (Converter) clazz.newInstance();
77         converter.setConverterManager(this);
78
79         LocalUtil.setParams(converter, params, ignore);
80
81         // add the converter for the specified match
82
addConverter(match, converter);
83     }
84
85     /* (non-Javadoc)
86      * @see org.directwebremoting.ConverterManager#addConverter(java.lang.String, org.directwebremoting.Converter)
87      */

88     public void addConverter(String JavaDoc match, Converter converter) throws IllegalArgumentException JavaDoc
89     {
90         // Check that we don't have this one already
91
Converter other = (Converter) converters.get(match);
92         if (other != null)
93         {
94             log.warn("Clash of converters for " + match + ". Using " + converter.getClass().getName() + " in place of " + other.getClass().getName());
95         }
96
97         log.debug("- adding converter: " + LocalUtil.getShortClassName(converter.getClass()) + " for " + match);
98         converters.put(match, converter);
99     }
100
101     /* (non-Javadoc)
102      * @see org.directwebremoting.ConverterManager#getConverterMatchStrings()
103      */

104     public Collection JavaDoc getConverterMatchStrings()
105     {
106         return Collections.unmodifiableSet(converters.keySet());
107     }
108
109     /* (non-Javadoc)
110      * @see org.directwebremoting.ConverterManager#getConverterByMatchString(java.lang.String)
111      */

112     public Converter getConverterByMatchString(String JavaDoc match)
113     {
114         return (Converter) converters.get(match);
115     }
116
117     /* (non-Javadoc)
118      * @see org.directwebremoting.ConverterManager#isConvertable(java.lang.Class)
119      */

120     public boolean isConvertable(Class JavaDoc paramType)
121     {
122         return getConverter(paramType) != null;
123     }
124
125     /* (non-Javadoc)
126      * @see org.directwebremoting.ConverterManager#convertInbound(java.lang.Class, org.directwebremoting.InboundVariable, org.directwebremoting.InboundContext, org.directwebremoting.TypeHintContext)
127      */

128     public Object JavaDoc convertInbound(Class JavaDoc paramType, InboundVariable iv, InboundContext inctx, TypeHintContext incc) throws MarshallException
129     {
130         Object JavaDoc converted = inctx.getConverted(iv, paramType);
131         if (converted == null)
132         {
133             // Was the inbound variable marshalled as an Object in the client
134
// (could mean that this is an instance of one of our generated
135
// JavaScript classes)
136
Converter converter = getNamedConverter(paramType, iv.getType());
137
138             // Fall back to the standard way of locating a converter if we
139
// didn't find anything above
140
if (converter == null)
141             {
142                 converter = getConverter(paramType);
143             }
144
145             if (converter == null)
146             {
147                 throw new MarshallException(paramType, Messages.getString("DefaultConverterManager.MissingConverter", paramType));
148             }
149
150             // We only think about doing a null conversion ourselves once we are
151
// sure that there is a converter available. This prevents hackers
152
// from passing null to things they are not allowed to convert
153
if (iv.isNull())
154             {
155                 return null;
156             }
157
158             inctx.pushContext(incc);
159             converted = converter.convertInbound(paramType, iv, inctx);
160             inctx.popContext();
161         }
162
163         return converted;
164     }
165
166     /* (non-Javadoc)
167      * @see org.directwebremoting.ConverterManager#convertOutbound(java.lang.Object, org.directwebremoting.OutboundContext)
168      */

169     public OutboundVariable convertOutbound(Object JavaDoc object, OutboundContext outctx) throws MarshallException
170     {
171         if (object == null)
172         {
173             return new SimpleOutboundVariable("null", outctx, true);
174         }
175
176         // Check to see if we have done this one already
177
OutboundVariable ov = outctx.get(object);
178         if (ov != null)
179         {
180             // So the object as been converted already, we just need to refer to it.
181
return ov.getReferenceVariable();
182         }
183
184         // So we will have to do the conversion
185
Converter converter = getConverter(object);
186         if (converter == null)
187         {
188             log.error(Messages.getString("DefaultConverterManager.MissingConverter", object.getClass().getName()));
189             return new SimpleOutboundVariable("null", outctx, true);
190         }
191
192         return converter.convertOutbound(object, outctx);
193     }
194
195     /* (non-Javadoc)
196      * @see org.directwebremoting.ConverterManager#setExtraTypeInfo(org.directwebremoting.TypeHintContext, java.lang.Class)
197      */

198     public void setExtraTypeInfo(TypeHintContext thc, Class JavaDoc type)
199     {
200         extraTypeInfoMap.put(thc, type);
201     }
202
203     /* (non-Javadoc)
204      * @see org.directwebremoting.ConverterManager#getExtraTypeInfo(org.directwebremoting.TypeHintContext)
205      */

206     public Class JavaDoc getExtraTypeInfo(TypeHintContext thc)
207     {
208         Class JavaDoc type = (Class JavaDoc) extraTypeInfoMap.get(thc);
209         return type;
210     }
211
212     /* (non-Javadoc)
213      * @see org.directwebremoting.ConverterManager#setConverters(java.util.Map)
214      */

215     public void setConverters(Map JavaDoc converters)
216     {
217         this.converters = converters;
218     }
219
220     /**
221      * Like <code>getConverter(object.getClass());</code> except that since the
222      * object can be null we check for that fist and then do a lookup against
223      * the <code>Void.TYPE</code> converter
224      * @param object The object to find a converter for
225      * @return The converter for the given type
226      */

227     private Converter getConverter(Object JavaDoc object)
228     {
229         if (object == null)
230         {
231             return getConverter(Void.TYPE);
232         }
233
234         return getConverter(object.getClass());
235     }
236
237     /**
238      * When we are using typed Javascript names we sometimes want to get a
239      * specially named converter
240      * @param paramType The class that we are converting to
241      * @param type The type name as passed in from the client
242      * @return The Converter that matches this request (if any)
243      * @throws MarshallException
244      */

245     protected Converter getNamedConverter(Class JavaDoc paramType, String JavaDoc type) throws MarshallException
246     {
247         if (type.startsWith("Object_"))
248         {
249             // Extract the JavaScript classname from the inbound type
250
String JavaDoc javascriptClassName = type.substring("Object_".length());
251
252             // Locate a converter for this JavaScript classname
253
Iterator JavaDoc it = converters.entrySet().iterator();
254             while (it.hasNext())
255             {
256                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
257                 String JavaDoc match = (String JavaDoc) entry.getKey();
258                 Converter conv = (Converter) entry.getValue();
259
260                 // JavaScript mapping is only applicable for compound converters
261
if (conv instanceof NamedConverter)
262                 {
263                     NamedConverter boConv = (NamedConverter) conv;
264                     if (boConv.getJavascript() != null && boConv.getJavascript().equals(javascriptClassName))
265                     {
266                         // We found a potential converter! But is the
267
// converter's Java class compatible with the
268
// parameter type?
269
try
270                         {
271                             Class JavaDoc inboundClass = LocalUtil.classForName(match);
272                             if (paramType.isAssignableFrom(inboundClass))
273                             {
274                                 // Hack: We also want to make sure that the
275
// converter creates its object based on the
276
// inbound class instead of the parameter
277
// type, and we have to use the other ref
278
// for this:
279
boConv.setInstanceType(inboundClass);
280                                 return boConv;
281                             }
282                         }
283                         catch (ClassNotFoundException JavaDoc ex)
284                         {
285                             throw new MarshallException(paramType, ex);
286                         }
287                     }
288                 }
289             }
290         }
291
292         return null;
293     }
294
295     /**
296      * @param paramType The type to find a converter for
297      * @return The converter for the given type, or null if one can't be found
298      */

299     private Converter getConverter(Class JavaDoc paramType)
300     {
301         // Can we find a converter assignable to paramType in the HashMap?
302
Converter converter = getConverterAssignableFrom(paramType);
303         if (converter != null)
304         {
305             return converter;
306         }
307
308         String JavaDoc lookup = paramType.getName();
309
310         // Before we start trying for a match on package parts we check for
311
// dynamic proxies
312
if (lookup.startsWith("$Proxy"))
313         {
314             converter = (Converter) converters.get("$Proxy*");
315             if (converter != null)
316             {
317                 return converter;
318             }
319         }
320
321         while (true)
322         {
323             // Can we find a converter using wildcards?
324
converter = (Converter) converters.get(lookup + ".*");
325             if (converter != null)
326             {
327                 return converter;
328             }
329
330             // Arrays can have wildcards like [L* so we don't require a '.'
331
converter = (Converter) converters.get(lookup + '*');
332             if (converter != null)
333             {
334                 return converter;
335             }
336
337             // Give up if the name is now empty
338
if (lookup.length() == 0)
339             {
340                 break;
341             }
342
343             // Strip of the component after the last .
344
int lastdot = lookup.lastIndexOf('.');
345             if (lastdot != -1)
346             {
347                 lookup = lookup.substring(0, lastdot);
348             }
349             else
350             {
351                 int arrayMarkers = 0;
352                 while (lookup.charAt(arrayMarkers) == '[')
353                 {
354                     arrayMarkers++;
355                 }
356
357                 if (arrayMarkers == 0)
358                 {
359                     // so we are out of dots and out of array markers
360
// bail out.
361
break;
362                 }
363
364                 // We want to keep the type marker too
365
lookup = lookup.substring(arrayMarkers - 1, arrayMarkers + 1);
366
367                 // Now can we find it?
368
converter = (Converter) converters.get(lookup);
369                 if (converter != null)
370                 {
371                     return converter;
372                 }
373             }
374         }
375
376         return null;
377     }
378
379     /**
380      * @param paramType The type to find a converter for
381      * @return The converter assignable for the given type, or null if one can't be found
382      */

383     private Converter getConverterAssignableFrom(Class JavaDoc paramType)
384     {
385         if (paramType == null)
386         {
387             return null;
388         }
389
390         String JavaDoc lookup = paramType.getName();
391
392         // Can we find the converter for paramType in the converters HashMap?
393
Converter converter = (Converter) converters.get(lookup);
394         if (converter != null)
395         {
396             return converter;
397         }
398
399         // Lookup all of the interfaces of this class for a match
400
Class JavaDoc[] interfaces = paramType.getInterfaces();
401         for (int i = 0; i < interfaces.length; i++)
402         {
403             converter = getConverterAssignableFrom(interfaces[i]);
404             if (converter != null)
405             {
406                 converters.put(lookup, converter);
407                 return converter;
408             }
409         }
410
411         // Let's search it in paramType superClass
412
converter = getConverterAssignableFrom(paramType.getSuperclass());
413         if (converter != null)
414         {
415             converters.put(lookup, converter);
416         }
417
418         return converter;
419     }
420
421     /**
422      * Where we store real type information behind generic types
423      */

424     private Map JavaDoc extraTypeInfoMap = new HashMap JavaDoc();
425
426     /**
427      * The log stream
428      */

429     private static final Logger log = Logger.getLogger(DefaultConverterManager.class);
430
431     /**
432      * The list of the available converters
433      */

434     private Map JavaDoc converterTypes = new HashMap JavaDoc();
435
436     /**
437      * The list of the configured converters
438      */

439     private Map JavaDoc converters = new HashMap JavaDoc();
440
441     /**
442      * The properties that we don't warn about if they don't exist.
443      * @see DefaultConverterManager#addConverter(String, String, Map)
444      */

445     private static List JavaDoc ignore = Arrays.asList(new String JavaDoc[] { "converter", "match" });
446 }
447
Popular Tags