KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > freemarker > ext > jython > JythonWrapper


1 /*
2  * Copyright (c) 2003 The Visigoth Software Society. All rights
3  * reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowledgement:
19  * "This product includes software developed by the
20  * Visigoth Software Society (http://www.visigoths.org/)."
21  * Alternately, this acknowledgement may appear in the software itself,
22  * if and wherever such third-party acknowledgements normally appear.
23  *
24  * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
25  * project contributors may be used to endorse or promote products derived
26  * from this software without prior written permission. For written
27  * permission, please contact visigoths@visigoths.org.
28  *
29  * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
30  * nor may "FreeMarker" or "Visigoth" appear in their names
31  * without prior written permission of the Visigoth Software Society.
32  *
33  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36  * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
37  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  * ====================================================================
46  *
47  * This software consists of voluntary contributions made by many
48  * individuals on behalf of the Visigoth Software Society. For more
49  * information on the Visigoth Software Society, please see
50  * http://www.visigoths.org/
51  */

52
53 package freemarker.ext.jython;
54
55 import java.math.BigDecimal JavaDoc;
56 import java.math.BigInteger JavaDoc;
57 import java.util.ArrayList JavaDoc;
58 import java.util.Collection JavaDoc;
59 import java.util.List JavaDoc;
60 import java.util.Map JavaDoc;
61
62 import org.python.core.Py;
63 import org.python.core.PyDictionary;
64 import org.python.core.PyFloat;
65 import org.python.core.PyInteger;
66 import org.python.core.PyJavaInstance;
67 import org.python.core.PyLong;
68 import org.python.core.PyNone;
69 import org.python.core.PyObject;
70 import org.python.core.PySequence;
71 import org.python.core.PyString;
72 import org.python.core.PyStringMap;
73
74 import freemarker.ext.util.ModelCache;
75 import freemarker.ext.util.WrapperTemplateModel;
76 import freemarker.template.AdapterTemplateModel;
77 import freemarker.template.ObjectWrapper;
78 import freemarker.template.TemplateBooleanModel;
79 import freemarker.template.TemplateHashModel;
80 import freemarker.template.TemplateHashModelEx;
81 import freemarker.template.TemplateMethodModel;
82 import freemarker.template.TemplateMethodModelEx;
83 import freemarker.template.TemplateModel;
84 import freemarker.template.TemplateModelAdapter;
85 import freemarker.template.TemplateModelException;
86 import freemarker.template.TemplateNumberModel;
87 import freemarker.template.TemplateScalarModel;
88 import freemarker.template.TemplateSequenceModel;
89 import freemarker.template.utility.OptimizerUtil;
90
91 /**
92  * An object wrapper that wraps Jython objects into FreeMarker template models
93  * and vice versa.
94  * @version $Id: JythonWrapper.java,v 1.23 2005/06/12 19:03:06 szegedia Exp $
95  * @author Attila Szegedi
96  */

97 public class JythonWrapper implements ObjectWrapper
98 {
99     private static final Class JavaDoc PYOBJECT_CLASS = PyObject.class;
100     public static final JythonWrapper INSTANCE = new JythonWrapper();
101
102     private final ModelCache modelCache = new ModelCache(this);
103
104     private boolean attributesShadowItems = true;
105
106     public JythonWrapper()
107     {
108     }
109     
110     /**
111      * Sets whether this wrapper caches model instances. Default is false.
112      * When set to true, calling {@link #wrap(Object)} multiple times for
113      * the same object will return the same model.
114      */

115     public void setUseCache(boolean useCache)
116     {
117         modelCache.setUseCache(useCache);
118     }
119     
120     /**
121      * Sets whether attributes shadow items in wrapped objects. When true
122      * (this is the default value), <code>${object.name}</code> will first
123      * try to locate a python attribute with the specified name on the object
124      * using {@link PyObject#__findattr__(java.lang.String)}, and only if it
125      * doesn't find the attribute will it call
126      * {@link PyObject#__getitem__(org.python.core.PyObject)}.
127      * When set to false, the lookup order is reversed and items
128      * are looked up before attributes.
129      */

130     public synchronized void setAttributesShadowItems(boolean attributesShadowItems)
131     {
132         this.attributesShadowItems = attributesShadowItems;
133     }
134     
135     boolean isAttributesShadowItems()
136     {
137         return attributesShadowItems;
138     }
139     
140     /**
141      * Wraps the passed Jython object into a FreeMarker template model. If
142      * the object is not a Jython object, it is first coerced into one using
143      * {@link Py#java2py(java.lang.Object)}. {@link PyDictionary} and {@link
144      * PyStringMap} are wrapped into a hash model, {@link PySequence}
145      * descendants are wrapped into a sequence model, {@link PyInteger}, {@link
146      * PyLong}, and {@link PyFloat} are wrapped into a number model. All objects
147      * are wrapped into a scalar model (using {@link Object#toString()} and a
148      * boolean model (using {@link PyObject#__nonzero__()}. For internal
149      * general-purpose {@link PyObject}s returned from a call to {@link
150      * #unwrap(TemplateModel)}, the template model that was passed to
151      * <code>unwrap</code> is returned.
152      */

153     public TemplateModel wrap(Object JavaDoc obj)
154     {
155         if(obj == null) {
156             return null;
157         }
158         if (obj instanceof TemplateModel) {
159             return (TemplateModel)obj;
160         }
161         if(obj instanceof TemplateModelAdapter) {
162             return ((TemplateModelAdapter)obj).getTemplateModel();
163         }
164         boolean asHash = false;
165         boolean asSequence = false;
166         if(obj instanceof PyJavaInstance) {
167             Object JavaDoc jobj = ((PyJavaInstance)obj).__tojava__(java.lang.Object JavaDoc.class);
168             // FreeMarker-aware, Jython-wrapped Java objects are left intact
169
if(jobj instanceof TemplateModel) {
170                 return (TemplateModel)jobj;
171             }
172             if(jobj instanceof Map JavaDoc) {
173                 asHash = true;
174             }
175             else if(jobj instanceof Collection JavaDoc) {
176                 asSequence = true;
177                 // FIXME: This is an ugly hack, but AFAIK, there's no better
178
// solution if we want to have Sets and other non-List
179
// collections managed by this layer, as Jython quite clearly
180
// doesn't support sets.
181
if(!(jobj instanceof List JavaDoc)) {
182                     obj = new ArrayList JavaDoc((Collection JavaDoc)jobj);
183                 }
184             }
185         }
186         
187         // If it's not a PyObject, first make a PyObject out of it.
188
if(!(obj instanceof PyObject))
189         {
190             obj = Py.java2py(obj);
191         }
192         if(asHash || obj instanceof PyDictionary || obj instanceof PyStringMap)
193         {
194             return modelCache.getInstance(obj, JythonHashModel.FACTORY);
195         }
196         if(asSequence || obj instanceof PySequence)
197         {
198             return modelCache.getInstance(obj, JythonSequenceModel.FACTORY);
199         }
200         if(obj instanceof PyInteger || obj instanceof PyLong || obj instanceof PyFloat)
201         {
202             return modelCache.getInstance(obj, JythonNumberModel.FACTORY);
203         }
204         if(obj instanceof PyNone) {
205             return null;
206         }
207         return modelCache.getInstance(obj, JythonModel.FACTORY);
208     }
209     
210     /**
211      * Coerces a template model into a {@link PyObject}.
212      * @param model the model to coerce
213      * @return the coerced model.
214      * <ul>
215      * <li>
216      * <li>{@link AdapterTemplateModel}s (i.e. {@link freemarker.ext.beans.BeanModel}) are marshalled
217      * using the standard Python marshaller {@link Py#java2py(Object)} on
218      * the result of <code>getWrappedObject(PyObject.class)</code>s. The
219      * native JythonModel instances will just return the underlying PyObject.
220      * <li>All other models that are {@link TemplateScalarModel scalars} are
221      * marshalled as {@link PyString}.
222      * <li>All other models that are {@link TemplateNumberModel numbers} are
223      * marshalled using the standard Python marshaller
224      * {@link Py#java2py(Object)} on their underlying <code>Number</code></li>
225      * <li>All other models are marshalled to a generic internal
226      * <code>PyObject</code> subclass that'll correctly pass
227      * <code>__finditem__</code>, <code>__len__</code>,
228      * <code>__nonzero__</code>, and <code>__call__</code> invocations to
229      * appropriate hash, sequence, and method models.</li>
230      * </ul>
231      */

232     public PyObject unwrap(TemplateModel model) throws TemplateModelException
233     {
234         if(model instanceof AdapterTemplateModel) {
235             return Py.java2py(((AdapterTemplateModel)model).getAdaptedObject(
236                     PYOBJECT_CLASS));
237         }
238         if(model instanceof WrapperTemplateModel) {
239             return Py.java2py(((WrapperTemplateModel)model).getWrappedObject());
240         }
241
242         // Scalars are marshalled to PyString.
243
if(model instanceof TemplateScalarModel)
244         {
245             return new PyString(((TemplateScalarModel)model).getAsString());
246         }
247         
248         // Numbers are wrapped to Python built-in numeric types.
249
if(model instanceof TemplateNumberModel)
250         {
251             Number JavaDoc number = ((TemplateNumberModel)model).getAsNumber();
252             if(number instanceof BigDecimal JavaDoc)
253             {
254                 number = OptimizerUtil.optimizeNumberRepresentation(number);
255             }
256             if(number instanceof BigInteger JavaDoc)
257             {
258                 // Py.java2py can't automatically coerce a BigInteger into
259
// a PyLong. This will probably get fixed in later Jython
260
// release.
261
return new PyLong((BigInteger JavaDoc)number);
262             }
263             else
264             {
265                 return Py.java2py(number);
266             }
267         }
268         // Return generic TemplateModel-to-Python adapter
269
return new TemplateModelToJythonAdapter(model);
270     }
271
272     private class TemplateModelToJythonAdapter extends PyObject
273     implements TemplateModelAdapter
274     {
275         private final TemplateModel model;
276         
277         TemplateModelToJythonAdapter(TemplateModel model)
278         {
279             this.model = model;
280         }
281         
282         public TemplateModel getTemplateModel()
283         {
284             return model;
285         }
286         
287         public PyObject __finditem__(PyObject key)
288         {
289             if(key instanceof PyInteger)
290             {
291                 return __finditem__(((PyInteger)key).getValue());
292             }
293             return __finditem__(key.toString());
294         }
295
296         public PyObject __finditem__(String JavaDoc key)
297         {
298             if(model instanceof TemplateHashModel)
299             {
300                 try
301                 {
302                     return unwrap(((TemplateHashModel)model).get(key));
303                 }
304                 catch(TemplateModelException e)
305                 {
306                     throw Py.JavaError(e);
307                 }
308             }
309             throw Py.TypeError("item lookup on non-hash model (" + getModelClass() + ")");
310         }
311         
312         public PyObject __finditem__(int index)
313         {
314             if(model instanceof TemplateSequenceModel)
315             {
316                 try
317                 {
318                     return unwrap(((TemplateSequenceModel)model).get(index));
319                 }
320                 catch(TemplateModelException e)
321                 {
322                     throw Py.JavaError(e);
323                 }
324             }
325             throw Py.TypeError("item lookup on non-sequence model (" + getModelClass() + ")");
326         }
327         
328         public PyObject __call__(PyObject args[], String JavaDoc keywords[])
329         {
330             if(model instanceof TemplateMethodModel)
331             {
332                 boolean isEx = model instanceof TemplateMethodModelEx;
333                 List JavaDoc list = new ArrayList JavaDoc(args.length);
334                 try
335                 {
336                     for(int i = 0; i < args.length; ++i)
337                     {
338                         list.add(
339                             isEx
340                             ? (Object JavaDoc)wrap(args[i])
341                             : (Object JavaDoc)(
342                                 args[i] == null
343                                 ? null
344                                 : args[i].toString()));
345                     }
346                     return unwrap((TemplateModel) ((TemplateMethodModelEx)model).exec(list));
347                 }
348                 catch(TemplateModelException e)
349                 {
350                     throw Py.JavaError(e);
351                 }
352             }
353             throw Py.TypeError("call of non-method model (" + getModelClass() + ")");
354         }
355         
356         public int __len__()
357         {
358             try
359             {
360                 if(model instanceof TemplateSequenceModel)
361                 {
362                     return ((TemplateSequenceModel)model).size();
363                 }
364                 if(model instanceof TemplateHashModelEx)
365                 {
366                     return ((TemplateHashModelEx)model).size();
367                 }
368             }
369             catch(TemplateModelException e)
370             {
371                 throw Py.JavaError(e);
372             }
373             
374             return 0;
375         }
376         
377         public boolean __nonzero__()
378         {
379             try
380             {
381                 if(model instanceof TemplateBooleanModel)
382                 {
383                     return ((TemplateBooleanModel)model).getAsBoolean();
384                 }
385                 if(model instanceof TemplateSequenceModel)
386                 {
387                     return ((TemplateSequenceModel)model).size() > 0;
388                 }
389                 if(model instanceof TemplateHashModel)
390                 {
391                     return !((TemplateHashModelEx)model).isEmpty();
392                 }
393             }
394             catch(TemplateModelException e)
395             {
396                 throw Py.JavaError(e);
397             }
398             return false;
399         }
400         
401         private String JavaDoc getModelClass()
402         {
403             return model == null ? "null" : model.getClass().getName();
404         }
405     }
406 }
407
Popular Tags