KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > directwebremoting > convert > BasicObjectConverter


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.convert;
17
18 import java.util.ArrayList JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.StringTokenizer JavaDoc;
24 import java.util.TreeMap JavaDoc;
25
26 import org.directwebremoting.dwrp.ParseUtil;
27 import org.directwebremoting.dwrp.ProtocolConstants;
28 import org.directwebremoting.dwrp.ObjectOutboundVariable;
29 import org.directwebremoting.extend.ConverterManager;
30 import org.directwebremoting.extend.InboundContext;
31 import org.directwebremoting.extend.InboundVariable;
32 import org.directwebremoting.extend.MarshallException;
33 import org.directwebremoting.extend.NamedConverter;
34 import org.directwebremoting.extend.OutboundContext;
35 import org.directwebremoting.extend.OutboundVariable;
36 import org.directwebremoting.extend.Property;
37 import org.directwebremoting.extend.TypeHintContext;
38 import org.directwebremoting.util.LocalUtil;
39 import org.directwebremoting.util.Logger;
40 import org.directwebremoting.util.Messages;
41
42 /**
43  * BasicObjectConverter is a parent to {@link BeanConverter} and
44  * {@link ObjectConverter} an provides support for include and exclude lists,
45  * and instanceTypes.
46  * @author Joe Walker [joe at getahead dot ltd dot uk]
47  */

48 public abstract class BasicObjectConverter extends BaseV20Converter implements NamedConverter
49 {
50     /* (non-Javadoc)
51      * @see org.directwebremoting.Converter#convertInbound(java.lang.Class, org.directwebremoting.InboundVariable, org.directwebremoting.InboundContext)
52      */

53     public Object JavaDoc convertInbound(Class JavaDoc paramType, InboundVariable iv, InboundContext inctx) throws MarshallException
54     {
55         String JavaDoc value = iv.getValue();
56
57         // If the text is null then the whole bean is null
58
if (value.trim().equals(ProtocolConstants.INBOUND_NULL))
59         {
60             return null;
61         }
62
63         if (!value.startsWith(ProtocolConstants.INBOUND_MAP_START))
64         {
65             throw new MarshallException(paramType, Messages.getString("BeanConverter.FormatError", ProtocolConstants.INBOUND_MAP_START));
66         }
67
68         if (!value.endsWith(ProtocolConstants.INBOUND_MAP_END))
69         {
70             throw new MarshallException(paramType, Messages.getString("BeanConverter.FormatError", ProtocolConstants.INBOUND_MAP_START));
71         }
72
73         value = value.substring(1, value.length() - 1);
74
75         try
76         {
77             Object JavaDoc bean;
78             if (instanceType != null)
79             {
80                 bean = instanceType.newInstance();
81             }
82             else
83             {
84                 bean = paramType.newInstance();
85             }
86
87             // We should put the new object into the working map in case it
88
// is referenced later nested down in the conversion process.
89
if (instanceType != null)
90             {
91                 inctx.addConverted(iv, instanceType, bean);
92             }
93             else
94             {
95                 inctx.addConverted(iv, paramType, bean);
96             }
97
98             Map JavaDoc properties = getPropertyMapFromObject(bean, false, true);
99
100             // Loop through the properties passed in
101
Map JavaDoc tokens = extractInboundTokens(paramType, value);
102             for (Iterator JavaDoc it = tokens.entrySet().iterator(); it.hasNext();)
103             {
104                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
105                 String JavaDoc key = (String JavaDoc) entry.getKey();
106                 String JavaDoc val = (String JavaDoc) entry.getValue();
107
108                 Property property = (Property) properties.get(key);
109                 if (property == null)
110                 {
111                     log.warn("Missing java bean property to match javascript property: " + key + ". For causes see debug level logs:");
112
113                     log.debug("- The javascript may be refer to a property that does not exist");
114                     log.debug("- You may be missing the correct setter: set" + Character.toTitleCase(key.charAt(0)) + key.substring(1) + "()");
115                     log.debug("- The property may be excluded using include or exclude rules.");
116
117                     StringBuffer JavaDoc all = new StringBuffer JavaDoc();
118                     for (Iterator JavaDoc pit = properties.keySet().iterator(); pit.hasNext();)
119                     {
120                         all.append(pit.next());
121                         if (pit.hasNext())
122                         {
123                             all.append(',');
124                         }
125                     }
126                     log.debug("Fields exist for (" + all + ").");
127                     continue;
128                 }
129
130                 Class JavaDoc propType = property.getPropertyType();
131
132                 String JavaDoc[] split = ParseUtil.splitInbound(val);
133                 String JavaDoc splitValue = split[LocalUtil.INBOUND_INDEX_VALUE];
134                 String JavaDoc splitType = split[LocalUtil.INBOUND_INDEX_TYPE];
135
136                 InboundVariable nested = new InboundVariable(iv.getLookup(), null, splitType, splitValue);
137                 TypeHintContext incc = createTypeHintContext(inctx, property);
138
139                 Object JavaDoc output = converterManager.convertInbound(propType, nested, inctx, incc);
140                 property.setValue(bean, output);
141             }
142
143             return bean;
144         }
145         catch (MarshallException ex)
146         {
147             throw ex;
148         }
149         catch (Exception JavaDoc ex)
150         {
151             throw new MarshallException(paramType, ex);
152         }
153     }
154
155     /**
156      * {@link #convertInbound(Class, InboundVariable, InboundContext)} needs to
157      * create a {@link TypeHintContext} for the {@link Property} it is
158      * converting so that the type guessing system can do its work.
159      * <p>The method of generating a {@link TypeHintContext} is different for
160      * the {@link BeanConverter} and the {@link ObjectConverter}.
161      * @param inctx The parent context
162      * @param property The property being converted
163      * @return The new TypeHintContext
164      */

165     protected abstract TypeHintContext createTypeHintContext(InboundContext inctx, Property property);
166
167     /* (non-Javadoc)
168      * @see org.directwebremoting.Converter#convertOutbound(java.lang.Object, org.directwebremoting.OutboundContext)
169      */

170     public OutboundVariable convertOutbound(Object JavaDoc data, OutboundContext outctx) throws MarshallException
171     {
172         // Where we collect out converted children
173
Map JavaDoc ovs = new TreeMap JavaDoc();
174
175         // We need to do this before collecing the children to save recurrsion
176
ObjectOutboundVariable ov = new ObjectOutboundVariable(outctx);
177         outctx.put(data, ov);
178
179         try
180         {
181             Map JavaDoc properties = getPropertyMapFromObject(data, true, false);
182             for (Iterator JavaDoc it = properties.entrySet().iterator(); it.hasNext();)
183             {
184                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
185                 String JavaDoc name = (String JavaDoc) entry.getKey();
186                 Property property = (Property) entry.getValue();
187
188                 Object JavaDoc value = property.getValue(data);
189                 OutboundVariable nested = getConverterManager().convertOutbound(value, outctx);
190
191                 ovs.put(name, nested);
192             }
193         }
194         catch (MarshallException ex)
195         {
196             throw ex;
197         }
198         catch (Exception JavaDoc ex)
199         {
200             throw new MarshallException(data.getClass(), ex);
201         }
202
203         ov.init(ovs, getJavascript());
204
205         return ov;
206     }
207
208     /**
209      * Set a list of properties excluded from conversion
210      * @param excludes The space or comma separated list of properties to exclude
211      */

212     public void setExclude(String JavaDoc excludes)
213     {
214         if (inclusions != null)
215         {
216             throw new IllegalArgumentException JavaDoc(Messages.getString("BeanConverter.OnlyIncludeOrExclude"));
217         }
218
219         exclusions = new ArrayList JavaDoc();
220
221         String JavaDoc toSplit = LocalUtil.replace(excludes, ",", " ");
222         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(toSplit);
223         while (st.hasMoreTokens())
224         {
225             String JavaDoc rule = st.nextToken();
226             if (rule.startsWith("get"))
227             {
228                 log.warn("Exclusions are based on property names and not method names. '" + rule + "' starts with 'get' so it looks like a method name and not a property name.");
229             }
230
231             exclusions.add(rule);
232         }
233     }
234
235     /**
236      * Set a list of properties included from conversion
237      * @param includes The space or comma separated list of properties to exclude
238      */

239     public void setInclude(String JavaDoc includes)
240     {
241         if (exclusions != null)
242         {
243             throw new IllegalArgumentException JavaDoc(Messages.getString("BeanConverter.OnlyIncludeOrExclude"));
244         }
245
246         inclusions = new ArrayList JavaDoc();
247
248         String JavaDoc toSplit = LocalUtil.replace(includes, ",", " ");
249         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(toSplit);
250         while (st.hasMoreTokens())
251         {
252             String JavaDoc rule = st.nextToken();
253             if (rule.startsWith("get"))
254             {
255                 log.warn("Inclusions are based on property names and not method names. '" + rule + "' starts with 'get' so it looks like a method name and not a property name.");
256             }
257
258             inclusions.add(rule);
259         }
260     }
261
262     /**
263      * @param name The class name to use as an implementation of the converted bean
264      * @throws ClassNotFoundException If the given class can not be found
265      */

266     public void setImplementation(String JavaDoc name) throws ClassNotFoundException JavaDoc
267     {
268         setInstanceType(LocalUtil.classForName(name));
269     }
270
271     /* (non-Javadoc)
272      * @see org.directwebremoting.convert.NamedConverter#getInstanceType()
273      */

274     public Class JavaDoc getInstanceType()
275     {
276         return instanceType;
277     }
278
279     /* (non-Javadoc)
280      * @see org.directwebremoting.convert.NamedConverter#setInstanceType(java.lang.Class)
281      */

282     public void setInstanceType(Class JavaDoc instanceType)
283     {
284         this.instanceType = instanceType;
285     }
286
287     /* (non-Javadoc)
288      * @see org.directwebremoting.convert.BaseV20Converter#setConverterManager(org.directwebremoting.ConverterManager)
289      */

290     public void setConverterManager(ConverterManager converterManager)
291     {
292         this.converterManager = converterManager;
293     }
294
295     /**
296      * Accessor for the current ConverterManager
297      * @return the current ConverterManager
298      */

299     public ConverterManager getConverterManager()
300     {
301         return converterManager;
302     }
303
304     /**
305      * Check with the access rules to see if we are allowed to convert a property
306      * @param property The property to test
307      * @return true if the property may be marshalled
308      */

309     protected boolean isAllowedByIncludeExcludeRules(String JavaDoc property)
310     {
311         if (exclusions != null)
312         {
313             // Check each exclusions and return false if we get a match
314
for (Iterator JavaDoc it = exclusions.iterator(); it.hasNext();)
315             {
316                 String JavaDoc test = (String JavaDoc) it.next();
317                 if (property.equals(test))
318                 {
319                     return false;
320                 }
321             }
322
323             // So we passed all the exclusions. The setters enforce mutual
324
// exclusion between exclusions and inclusions so we don't need to
325
// 'return true' here, we can carry on. This has the advantage that
326
// we can relax the mutual exclusion at some stage.
327
}
328
329         if (inclusions != null)
330         {
331             // Check each inclusion and return true if we get a match
332
for (Iterator JavaDoc it = inclusions.iterator(); it.hasNext();)
333             {
334                 String JavaDoc test = (String JavaDoc) it.next();
335                 if (property.equals(test))
336                 {
337                     return true;
338                 }
339             }
340
341             // Since we are white-listing with inclusions and there was not
342
// match, this property is not allowed.
343
return false;
344         }
345
346         // default to allow if there are no inclusions or exclusions
347
return true;
348     }
349
350     /**
351      * Loop over all the inputs and extract a Map of key:value pairs
352      * @param paramType The type we are converting to
353      * @param value The input string
354      * @return A Map of the tokens in the string
355      * @throws MarshallException If the marshalling fails
356      */

357     protected Map JavaDoc extractInboundTokens(Class JavaDoc paramType, String JavaDoc value) throws MarshallException
358     {
359         Map JavaDoc tokens = new HashMap JavaDoc();
360         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(value, ProtocolConstants.INBOUND_MAP_SEPARATOR);
361         int size = st.countTokens();
362
363         for (int i = 0; i < size; i++)
364         {
365             String JavaDoc token = st.nextToken();
366             if (token.trim().length() == 0)
367             {
368                 continue;
369             }
370
371             int colonpos = token.indexOf(ProtocolConstants.INBOUND_MAP_ENTRY);
372             if (colonpos == -1)
373             {
374                 throw new MarshallException(paramType, Messages.getString("BeanConverter.MissingSeparator", ProtocolConstants.INBOUND_MAP_ENTRY, token));
375             }
376
377             String JavaDoc key = token.substring(0, colonpos).trim();
378             String JavaDoc val = token.substring(colonpos + 1).trim();
379             tokens.put(key, val);
380         }
381
382         return tokens;
383     }
384
385     /* (non-Javadoc)
386      * @see org.directwebremoting.convert.NamedConverter#getJavascript()
387      */

388     public String JavaDoc getJavascript()
389     {
390         return javascript;
391     }
392
393     /* (non-Javadoc)
394      * @see org.directwebremoting.convert.NamedConverter#setJavascript(java.lang.String)
395      */

396     public void setJavascript(String JavaDoc javascript)
397     {
398         this.javascript = javascript;
399     }
400
401     /**
402      * The javascript class name for the converted objects
403      */

404     protected String JavaDoc javascript;
405
406     /**
407      * The list of excluded properties
408      */

409     protected List JavaDoc exclusions = null;
410
411     /**
412      * The list of included properties
413      */

414     protected List JavaDoc inclusions = null;
415
416     /**
417      * A type that allows us to fulfill an interface or subtype requirement
418      */

419     protected Class JavaDoc instanceType = null;
420
421     /**
422      * To forward marshalling requests
423      */

424     protected ConverterManager converterManager = null;
425
426     /**
427      * The log stream
428      */

429     private static final Logger log = Logger.getLogger(BasicObjectConverter.class);
430 }
431
Popular Tags