Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Copyright (c) 2003 The Visigoth Software Society. All rights
    * reserved.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions
    * are met:
    *
    * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowledgement:
   *       "This product includes software developed by the
   *        Visigoth Software Society (http://www.visigoths.org/)."
   *    Alternately, this acknowledgement may appear in the software itself,
   *    if and wherever such third-party acknowledgements normally appear.
   *
   * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the 
   *    project contributors may be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact visigoths@visigoths.org.
   *
   * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
   *    nor may "FreeMarker" or "Visigoth" appear in their names
   *    without prior written permission of the Visigoth Software Society.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Visigoth Software Society. For more
   * information on the Visigoth Software Society, please see
   * http://www.visigoths.org/
   */
  
  package freemarker.ext.beans;
  
  import java.util.Arrays;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
Utility class that provides generic services to reflection classes. It handles all polymorphism issues in the wrap(java.lang.Object) and unwrap(freemarker.template.TemplateModel) methods.

Author(s):
Attila Szegedi
Version:
$Id: BeansWrapper.java,v 1.91.2.13 2007/04/02 13:08:59 szegedia Exp $
 
 public class BeansWrapper implements ObjectWrapper
 {
     static final Object CAN_NOT_UNWRAP = new Object();
     private static final Class BIGINTEGER_CLASS = java.math.BigInteger.class;
     private static final Class BOOLEAN_CLASS = Boolean.class;
     private static final Class CHARACTER_CLASS = Character.class;
     private static final Class COLLECTION_CLASS = Collection.class;
     private static final Class DATE_CLASS = Date.class;
     private static final Class HASHADAPTER_CLASS = HashAdapter.class;
     private static final Class ITERABLE_CLASS;
     private static final Class LIST_CLASS = List.class;
     private static final Class MAP_CLASS = Map.class;
     private static final Class NUMBER_CLASS = Number.class;
     private static final Class OBJECT_CLASS = Object.class;
     private static final Class SEQUENCEADAPTER_CLASS = SequenceAdapter.class;
     private static final Class SET_CLASS = Set.class;
     private static final Class SETADAPTER_CLASS = SetAdapter.class;
     private static final Class STRING_CLASS = String.class;
     static {
         Class iterable;
         try {
             iterable = Class.forName("java.lang.Iterable");
         }
         catch(ClassNotFoundException e) {
             // We're running on a pre-1.5 JRE
             iterable = null;
         }
          = iterable;
     }
     
     // When this property is true, some things are stricter. This is mostly to
     // catch anomalous things in development that can otherwise be valid situations
     // for our users.
     private static final boolean DEVELOPMENT = "true".equals(SecurityUtilities.getSystemProperty("freemarker.development"));
     
     private static final Constructor ENUMS_MODEL_CTOR = enumsModelCtor();
 
     private static final Logger logger = Logger.getLogger("freemarker.beans");
     
     private static final Set UNSAFE_METHODS = createUnsafeMethodsSet();
     
     static final Object GENERIC_GET_KEY = new Object();
     private static final Object CONSTRUCTORS = new Object();
     private static final Object ARGTYPES = new Object();
 
     private static final boolean javaRebelAvailable = isJavaRebelAvailable();
    
    
The default instance of BeansWrapper
 
     private static final BeansWrapper INSTANCE = new BeansWrapper();
 
     // Cache of hash maps that contain already discovered properties and methods
     // for a specified class. Each key is a Class, each value is a hash map. In
     // that hash map, each key is a property/method name, each value is a
     // MethodDescriptor or a PropertyDescriptor assigned to that property/method.
     private final Map classCache = new HashMap();
     private Set cachedClassNames = new HashSet();
 
     private final StaticModels staticModels = new StaticModels(this);
     private final ClassBasedModelFactory enumModels = createEnumModels(this);
 
     private final ModelCache modelCache = new BeansModelCache(this);
     
     private final BooleanModel FALSE = new BooleanModel(.this);
     private final BooleanModel TRUE = new BooleanModel(.this);

    
At this level of exposure, all methods and properties of the wrapped objects are exposed to the template.
 
     public static final int EXPOSE_ALL = 0;
    
    
At this level of exposure, all methods and properties of the wrapped objects are exposed to the template except methods that are deemed not safe. The not safe methods are java.lang.Object methods wait() and notify(), java.lang.Class methods getClassLoader() and newInstance(), java.lang.reflect.Method and java.lang.reflect.Constructor invoke() and newInstance() methods, all java.lang.reflect.Field set methods, all java.lang.Thread and java.lang.ThreadGroup methods that can change its state, as well as the usual suspects in java.lang.System and java.lang.Runtime.
 
     public static final int EXPOSE_SAFE = 1;
    
    
At this level of exposure, only property getters are exposed. Additionally, property getters that map to unsafe methods are not exposed (i.e. Class.classLoader and Thread.contextClassLoader).
 
     public static final int EXPOSE_PROPERTIES_ONLY = 2;

    
At this level of exposure, no bean properties and methods are exposed. Only map items, resource bundle items, and objects retrieved through the generic get method (on objects of classes that have a generic get method) can be retrieved through the hash interface. You might want to call setMethodsShadowItems(boolean) with false value to speed up map item retrieval.
 
     public static final int EXPOSE_NOTHING = 3;
 
     private int exposureLevel = ;
     private TemplateModel nullModel = null;
     private boolean methodsShadowItems = true;
     private boolean exposeFields = false;
     private int defaultDateType = .;
 
     private ObjectWrapper outerIdentity = this;
     private boolean simpleMapWrapper;
     private boolean strict = false;
    
    
Creates a new instance of BeansWrapper. The newly created instance will use the null reference as its null object, it will use EXPOSE_SAFE method exposure level, and will not cache model instances.
 
     public BeansWrapper() {
         if() {
             JavaRebelIntegration.registerWrapper(this);
         }
     }
    
    
 
     public boolean isStrict() {
     	return ;
     }
    
    
Specifies if an attempt to read a bean property that doesn't exist in the wrapped object should throw an InvalidPropertyException.

If this property is false (the default) then an attempt to read a missing bean property is the same as reading an existing bean property whose value is null. The template can't tell the difference, and thus always can use ?default('something') and ?exists and similar built-ins to handle the situation.

If this property is true then an attempt to read a bean propertly in the template (like myBean.aProperty) that doesn't exist in the bean object (as opposed to just holding null value) will cause InvalidPropertyException, which can't be suppressed in the template (not even with myBean.noSuchProperty?default('something')). This way ?default('something') and ?exists and similar built-ins can be used to handle existing properties whose value is null, without the risk of hiding typos in the property names. Typos will always cause error. But mind you, it goes against the basic approach of FreeMarker, so use this feature only if you really know what are you doing.

 
     public void setStrict(boolean strict) {
     	this. = strict;
     }

    
When wrapping an object, the BeansWrapper commonly needs to wrap "sub-objects", for example each element in a wrapped collection. Normally it wraps these objects using itself. However, this makes it difficult to delegate to a BeansWrapper as part of a custom aggregate ObjectWrapper. This method lets you set the ObjectWrapper which will be used to wrap the sub-objects.

Parameters:
outerIdentity the aggregate ObjectWrapper
 
     public void setOuterIdentity(ObjectWrapper outerIdentity)
     {
         this. = outerIdentity;
     }

    
By default returns this.

 
     public ObjectWrapper getOuterIdentity()
     {
         return ;
     }

    
By default the BeansWrapper wraps classes implementing java.util.Map using MapModel. Setting this flag will cause it to use a SimpleMapModel instead. The biggest difference is that when using a SimpleMapModel, the map will be visible as TemplateHashModelEx, and the subvariables will be the content of the map, without the other methods and properties of the map object.

Parameters:
simpleMapWrapper enable simple map wrapping
 
     public void setSimpleMapWrapper(boolean simpleMapWrapper)
     {
         this. = simpleMapWrapper;
     }

    
Tells whether Maps are exposed as simple maps, without access to their method. See setSimpleMapWrapper(boolean) for details.

Returns:
true if Maps are exposed as simple hashes, false if they're exposed as full JavaBeans.
 
     public boolean isSimpleMapWrapper()
     {
         return ;
     }

    
Sets the method exposure level. By default, set to EXPOSE_SAFE.

Parameters:
exposureLevel can be any of the EXPOSE_xxx constants.
 
     public void setExposureLevel(int exposureLevel)
     {
         if(exposureLevel <  || exposureLevel > )
         {
             throw new IllegalArgumentException("Illegal exposure level " + exposureLevel);
         }
         this. = exposureLevel;
     }
     
     int getExposureLevel()
     {
         return ;
     }
    
    
Controls whether public instance fields of classes are exposed to templates.

Parameters:
exposeFields if set to true, public instance fields of classes that do not have a property getter defined can be accessed directly by their name. If there is a property getter for a property of the same name as the field (i.e. getter "getFoo()" and field "foo"), then referring to "foo" in template invokes the getter. If set to false, no access to public instance fields of classes is given. Default is false.
 
     public void setExposeFields(boolean exposeFields)
     {
         this. = exposeFields;
     }
    
    
Returns whether exposure of public instance fields of classes is enabled. See setExposeFields(boolean) for details.

Returns:
true if public instance fields are exposed, false otherwise.
 
     public boolean isExposeFields()
     {
         return ;
     }
    
    
Sets whether methods shadow items in beans. When true (this is the default value), ${object.name} will first try to locate a bean method or property with the specified name on the object, and only if it doesn't find it will it try to call object.get(name), the so-called "generic get method" that is usually used to access items of a container (i.e. elements of a map). When set to false, the lookup order is reversed and generic get method is called first, and only if it returns null is method lookup attempted.
 
     public synchronized void setMethodsShadowItems(boolean methodsShadowItems)
     {
         this. = methodsShadowItems;
     }
     
     boolean isMethodsShadowItems()
     {
         return ;
     }
    
    
Sets the default date type to use for date models that result from a plain java.util.Date instead of java.sql.Date or java.sql.Time or java.sql.Timestamp. Default value is freemarker.template.TemplateDateModel.UNKNOWN.

Parameters:
defaultDateType the new default date type.
 
     public synchronized void setDefaultDateType(int defaultDateType) {
         this. = defaultDateType;
     }

    
Returns the default date type. See setDefaultDateType(int) for details.

Returns:
the default date type
 
     protected int getDefaultDateType() {
         return ;
     }
    
    
Sets whether this wrapper caches model instances. Default is false. When set to true, calling wrap(java.lang.Object) multiple times for the same object will likely return the same model (although there is no guarantee as the cache items can be cleared anytime).
 
     public void setUseCache(boolean useCache)
     {
         .setUseCache(useCache);
     }
    
    
Sets the null model. This model is returned from the wrap(java.lang.Object) method whenever the underlying object reference is null. It defaults to null reference, which is dealt with quite strictly on engine level, however you can substitute an arbitrary (perhaps more lenient) model, such as freemarker.template.TemplateScalarModel.EMPTY_STRING.
 
     public void setNullModel(TemplateModel nullModel)
     {
         this. = nullModel;
     }
    
    
Returns the default instance of the wrapper. This instance is used when you construct various bean models without explicitly specifying a wrapper. It is also returned by freemarker.template.ObjectWrapper.BEANS_WRAPPER and this is the sole instance that is used by the JSP adapter. You can modify the properties of the default instance (caching, exposure level, null model) to affect its operation. By default, the default instance is not caching, uses the EXPOSE_SAFE exposure level, and uses null reference as the null model.
 
     public static final BeansWrapper getDefaultInstance()
     {
         return ;
     }

    
Wraps the object with a template model that is most specific for the object's class. Specifically:
 
     public TemplateModel wrap(Object objectthrows TemplateModelException
     {
         if(object == null)
             return ;
         return .getInstance(object);
     }

    

Deprecated:
override getModelFactory(java.lang.Class) instead. Using this method will now bypass wrapper caching (if it is enabled) and always result in creation of a new wrapper. This method will be removed in 2.4
Parameters:
object
factory
 
     protected TemplateModel getInstance(Object objectModelFactory factory)
     {
         return factory.create(objectthis);
     }
 
     private final ModelFactory BOOLEAN_FACTORY = new ModelFactory() {
         public TemplateModel create(Object objectObjectWrapper wrapper) {
             return ((Boolean)object).booleanValue() ?  : 
         }
     };
 
     private static final ModelFactory ITERATOR_FACTORY = new ModelFactory() {
         public TemplateModel create(Object objectObjectWrapper wrapper) {
             return new IteratorModel((Iterator)object, (BeansWrapper)wrapper); 
         }
     };
 
     private static final ModelFactory ENUMERATION_FACTORY = new ModelFactory() {
         public TemplateModel create(Object objectObjectWrapper wrapper) {
             return new EnumerationModel((Enumeration)object, (BeansWrapper)wrapper); 
         }
     };
 
     protected ModelFactory getModelFactory(Class clazz) {
         if(Map.class.isAssignableFrom(clazz)) {
             return  ? . : .;
         }
         if(Collection.class.isAssignableFrom(clazz)) {
             return .;
         }
         if(Number.class.isAssignableFrom(clazz)) {
             return .;
         }
         if(Date.class.isAssignableFrom(clazz)) {
             return .;
         }
         if(Boolean.class == clazz) { // Boolean is final 
             return ;
         }
         if(ResourceBundle.class.isAssignableFrom(clazz)) {
             return .;
         }
         if(Iterator.class.isAssignableFrom(clazz)) {
             return ;
         }
         if(Enumeration.class.isAssignableFrom(clazz)) {
             return ;
         }
         if(clazz.isArray()) {
             return .;
         }
         return .;
     }

    
Attempts to unwrap a model into underlying object. Generally, this method is the inverse of the wrap(java.lang.Object) method. In addition it will unwrap arbitrary freemarker.template.TemplateNumberModel instances into a number, arbitrary freemarker.template.TemplateDateModel instances into a date, freemarker.template.TemplateScalarModel instances into a String, arbitrary freemarker.template.TemplateBooleanModel instances into a Boolean, arbitrary freemarker.template.TemplateHashModel instances into a Map, arbitrary freemarker.template.TemplateSequenceModel into a List, and arbitrary freemarker.template.TemplateCollectionModel into a Set. All other objects are returned unchanged.

Throws:
freemarker.template.TemplateModelException if an attempted unwrapping fails.
 
     public Object unwrap(TemplateModel modelthrows TemplateModelException
     {
         return unwrap(model);
     }
    
    
Attempts to unwrap a model into an object of the desired class. Generally, this method is the inverse of the wrap(java.lang.Object) method. It recognizes a wide range of hint classes - all Java built-in primitives, primitive wrappers, numbers, dates, sets, lists, maps, and native arrays.

Parameters:
model the model to unwrap
hint the class of the unwrapped result
Returns:
the unwrapped result of the desired class
Throws:
freemarker.template.TemplateModelException if an attempted unwrapping fails.
 
     public Object unwrap(TemplateModel modelClass hint
     throws TemplateModelException
     {
         final Object obj = unwrapInternal(modelhint);
         if(obj == ) {
           throw new TemplateModelException("Can not unwrap model of type " + 
               model.getClass().getName() + " to type " + hint.getName());
         }
         return obj;
     }
     
     Object unwrapInternal(TemplateModel modelClass hint
     throws TemplateModelException
     {
         return unwrap(modelhintnull);
     }
 
     private Object unwrap(TemplateModel modelClass hintMap recursionStops
     throws TemplateModelException
     {
         if(model == null || model == ) {
             return null;
         }
         
         boolean isBoolean = . == hint;
         boolean isChar = . == hint;
         
         // This is for transparent interop with other wrappers (and ourselves)
         // Passing the hint allows i.e. a Jython-aware method that declares a
         // PyObject as its argument to receive a PyObject from a JythonModel
         // passed as an argument to TemplateMethodModelEx etc.
         if(model instanceof AdapterTemplateModel) {
             Object adapted = ((AdapterTemplateModel)model).getAdaptedObject(
                     hint);
             if(hint.isInstance(adapted)) {
                 return adapted;
             }
             // Attempt numeric conversion 
             if(adapted instanceof Number && ((hint.isPrimitive() && !isChar && 
                     !isBoolean) || .isAssignableFrom(hint))) {
                 Number number = convertUnwrappedNumber(hint,
                         (Number)adapted);
                 if(number != null) {
                     return number;
                 }
             }
         }
         
         if(model instanceof WrapperTemplateModel) {
             Object wrapped = ((WrapperTemplateModel)model).getWrappedObject();
             if(hint.isInstance(wrapped)) {
                 return wrapped;
             }
             // Attempt numeric conversion 
             if(wrapped instanceof Number && ((hint.isPrimitive() && !isChar && 
                     !isBoolean) || .isAssignableFrom(hint))) {
                 Number number = convertUnwrappedNumber(hint,
                         (Number)wrapped);
                 if(number != null) {
                     return number;
                 }
             }
         }
         
         // Translation of generic template models to POJOs. First give priority
         // to various model interfaces based on the hint class. This helps us
         // select the appropriate interface in multi-interface models when we
         // know what is expected as the return type.
 
         if( == hint) {
             if(model instanceof TemplateScalarModel) {
                 return ((TemplateScalarModel)model).getAsString();
             }
             // String is final, so no other conversion will work
             return ;
         }
 
         // Primitive numeric types & Number.class and its subclasses
         if((hint.isPrimitive() && !isChar && !isBoolean
                 || .isAssignableFrom(hint)) {
             if(model instanceof TemplateNumberModel) {
                 Number number = convertUnwrappedNumber(hint
                         ((TemplateNumberModel)model).getAsNumber());
                 if(number != null) {
                     return number;
                 }
             }
         }
         
         if(isBoolean ||  == hint) {
             if(model instanceof TemplateBooleanModel) {
                 return ((TemplateBooleanModel)model).getAsBoolean() 
                 ? . : .;
             }
             // Boolean is final, no other conversion will work
             return ;
         }
 
         if( == hint) {
             if(model instanceof TemplateHashModel) {
                 return new HashAdapter((TemplateHashModel)modelthis);
             }
         }
         
         if( == hint) {
             if(model instanceof TemplateSequenceModel) {
                 return new SequenceAdapter((TemplateSequenceModel)modelthis);
             }
         }
         
         if( == hint) {
             if(model instanceof TemplateCollectionModel) {
                 return new SetAdapter((TemplateCollectionModel)modelthis);
             }
         }
         
         if( == hint 
                 ||  == hint) {
             if(model instanceof TemplateCollectionModel) {
                 return new CollectionAdapter((TemplateCollectionModel)model
                         this);
             }
             if(model instanceof TemplateSequenceModel) {
                 return new SequenceAdapter((TemplateSequenceModel)modelthis);
             }
         }
         
         // TemplateSequenceModels can be converted to arrays
         if(hint.isArray()) {
             if(model instanceof TemplateSequenceModel) {
                 if(recursionStops != null) {
                     Object retval = recursionStops.get(model);
                     if(retval != null) {
                         return retval;
                     }
                 } else {
                     recursionStops = 
                         new IdentityHashMap();
                 }
                 TemplateSequenceModel seq = (TemplateSequenceModel)model;
                 Class componentType = hint.getComponentType();
                 Object array = Array.newInstance(componentTypeseq.size());
                 recursionStops.put(modelarray);
                 try {
                     int size = seq.size();
                     for (int i = 0; i < sizei++) {
                         Object val = unwrap(seq.get(i), componentType
                                 recursionStops);
                         if(val == ) {
                             return ;
                         }
                         Array.set(arrayival);
                     }
                 } finally {
                     recursionStops.remove(model);
                 }
                 return array;
             }
             // array classes are final, no other conversion will work
             return ;
         }
         
         // Allow one-char strings to be coerced to characters
         if(isChar || hint == ) {
             if(model instanceof TemplateScalarModel) {
                 String s = ((TemplateScalarModel)model).getAsString();
                 if(s.length() == 1) {
                     return new Character(s.charAt(0));
                 }
             }
             // Character is final, no other conversion will work
             return ;
         }
 
         if(.isAssignableFrom(hint)) {
             if(model instanceof TemplateDateModel) {
                 Date date = ((TemplateDateModel)model).getAsDate();
                 if(hint.isInstance(date)) {
                     return date;
                 }
             }
         }
         
         // Translation of generic template models to POJOs. Since hint was of
         // no help initially, now use an admittedly arbitrary order of 
         // interfaces. Note we still test for isInstance and isAssignableFrom
         // to guarantee we return a compatible value. 
         if(model instanceof TemplateNumberModel) {
             Number number = ((TemplateNumberModel)model).getAsNumber();
             if(hint.isInstance(number)) {
                 return number;
             }
         }
         if(model instanceof TemplateDateModel) {
             Date date = ((TemplateDateModel)model).getAsDate();
             if(hint.isInstance(date)) {
                 return date;
             }
         }
         if(model instanceof TemplateScalarModel && 
                 hint.isAssignableFrom()) {
             return ((TemplateScalarModel)model).getAsString();
         }
         if(model instanceof TemplateBooleanModel && 
                 hint.isAssignableFrom()) {
             return ((TemplateBooleanModel)model).getAsBoolean() 
             ? . : .;
         }
         if(model instanceof TemplateHashModel && hint.isAssignableFrom(
                 )) {
             return new HashAdapter((TemplateHashModel)modelthis);
         }
         if(model instanceof TemplateSequenceModel 
                 && hint.isAssignableFrom()) {
             return new SequenceAdapter((TemplateSequenceModel)modelthis);
         }
         if(model instanceof TemplateCollectionModel && 
                 hint.isAssignableFrom()) {
             return new SetAdapter((TemplateCollectionModel)modelthis);
         }
 
         // Last ditch effort - is maybe the model itself instance of the 
         // required type?
         if(hint.isInstance(model)) {
             return model;
         }
         
         return ;
     }
 
     private static Number convertUnwrappedNumber(Class hintNumber number)
     {
         if(hint == . || hint == Integer.class) {
             return number instanceof Integer ? (Integer)number : 
                 new Integer(number.intValue());
         }
         if(hint == . || hint == Long.class) {
             return number instanceof Long ? (Long)number : 
                 new Long(number.longValue());
         }
         if(hint == . || hint == Float.class) {
             return number instanceof Float ? (Float)number : 
                 new Float(number.floatValue());
         }
         if(hint == . 
                 || hint == Double.class) {
             return number instanceof Double ? (Double)number : 
                 new Double(number.doubleValue());
         }
         if(hint == . || hint == Byte.class) {
             return number instanceof Byte ? (Byte)number : 
                 new Byte(number.byteValue());
         }
         if(hint == . || hint == Short.class) {
             return number instanceof Short ? (Short)number : 
                 new Short(number.shortValue());
         }
         if(hint == BigInteger.class) {
             return number instanceof BigInteger ? number : 
                 new BigInteger(number.toString());
         }
         if(hint == BigDecimal.class) {
             if(number instanceof BigDecimal) {
                 return number;
             }
             if(number instanceof BigInteger) {
                 return new BigDecimal((BigInteger)number);
             }
             if(number instanceof Long) {
                 // Because we can't represent long accurately as a 
                 // double
                 return new BigDecimal(number.toString());
             }
             return new BigDecimal(number.doubleValue());
         }
         // Handle nonstandard Number subclasses as well as directly 
         // java.lang.Number too
         if(hint.isInstance(number)) {
             return number;
         }
         return null;
     }
    
    
Invokes the specified method, wrapping the return value. The specialty of this method is that if the return value is null, and the return type of the invoked method is void, freemarker.template.TemplateModel.NOTHING is returned.

Parameters:
object the object to invoke the method on
method the method to invoke
args the arguments to the method
Returns:
the wrapped return value of the method.
Throws:
java.lang.reflect.InvocationTargetException if the invoked method threw an exception
java.lang.IllegalAccessException if the method can't be invoked due to an access restriction.
freemarker.template.TemplateModelException if the return value couldn't be wrapped (this can happen if the wrapper has an outer identity or is subclassed, and the outer identity or the subclass throws an exception. Plain BeansWrapper never throws TemplateModelException).
 
     TemplateModel invokeMethod(Object objectMethod methodObject[] args)
     throws
         InvocationTargetException,
         IllegalAccessException,
         TemplateModelException
     {
         Object retval = method.invoke(objectargs);
         return 
             method.getReturnType() == . 
             ? .
             : getOuterIdentity().wrap(retval); 
     }

   
Returns a hash model that represents the so-called class static models. Every class static model is itself a hash through which you can call static methods on the specified class. To obtain a static model for a class, get the element of this hash with the fully qualified class name. For example, if you place this hash model inside the root data model under name "statics", you can use i.e. statics["java.lang. System"]. currentTimeMillis() to call the java.lang.System.currentTimeMillis() method.

Returns:
a hash model whose keys are fully qualified class names, and that returns hash models whose elements are the static models of the classes.
 
     {
         return ;
     }
    
    
    
Returns a hash model that represents the so-called class enum models. Every class' enum model is itself a hash through which you can access enum value declared by the specified class, assuming that class is an enumeration. To obtain an enum model for a class, get the element of this hash with the fully qualified class name. For example, if you place this hash model inside the root data model under name "enums", you can use i.e. statics["java.math.RoundingMode"].UP to access the java.math.RoundingMode.UP value.

Returns:
a hash model whose keys are fully qualified class names, and that returns hash models whose elements are the enum models of the classes.
Throws:
java.lang.UnsupportedOperationException if this method is invoked on a pre-1.5 JRE, as Java enums aren't supported there.
 
     public TemplateHashModel getEnumModels() {
         if( == null) {
             throw new UnsupportedOperationException(
                     "Enums not supported on pre-1.5 JRE");
         }
         return ;
     }
 
     public Object newInstance(Class clazzList arguments)
     throws
         TemplateModelException
     {
         try
         {
             introspectClass(clazz);
             Map classInfo = (Map).get(clazz);
             Object ctors = classInfo.get();
             if(ctors == null)
             {
                 throw new TemplateModelException("Class " + clazz.getName() + 
                         " has no public constructors.");
             }
             Constructor ctor = null;
             Object[] objargs;
             if(ctors instanceof SimpleMemberModel)
             {
                 SimpleMemberModel smm = (SimpleMemberModel)ctors;
                 ctor = (Constructor)smm.getMember();
                 objargs = smm.unwrapArguments(argumentsthis);
             }
             else if(ctors instanceof MethodMap)
             {
                 MethodMap methodMap = (MethodMap)ctors
                 MemberAndArguments maa = 
                     methodMap.getMemberAndArguments(arguments);
                 objargs = maa.getArgs();
                 ctor = (Constructor)maa.getMember();
             }
             else
             {
                 // Cannot happen
                 throw new Error();
             }
             return ctor.newInstance(objargs);
         }
         catch (TemplateModelException e)
         {
             throw e;
         }
         catch (Exception e)
         {
             throw new TemplateModelException(
                     "Could not create instance of class " + clazz.getName(), e);
         }
     }
     
     void introspectClass(Class clazz)
     {
         synchronized()
         {
             if(!.containsKey(clazz))
             {
                 introspectClassInternal(clazz);
             }
         }
     }
     
     void removeIntrospectionInfo(Class clazz) {
         synchronized() {
             .remove(clazz);
             .removeIntrospectionInfo(clazz);
             if( != null) {
                 .removeIntrospectionInfo(clazz);
             }
             .remove(clazz.getName());
             synchronized(this) {
                 .clearCache();
             }
         }
     }
 
     private void introspectClassInternal(Class clazz)
     {
         String className = clazz.getName();
         if(.contains(className))
         {
             if(.isInfoEnabled())
             {
                 .info("Detected a reloaded class [" + className + 
                         "]. Clearing BeansWrapper caches.");
             }
             // Class reload detected, throw away caches
             .clear();
             = new HashSet();
            synchronized(this)
            {
                .clearCache();
            }
            .clearCache();
            if( != null) {
                .clearCache();
            }
        }
        .put(clazzpopulateClassMap(clazz));
        .add(className);
    }
    Map getClassKeyMap(Class clazz)
    {
        Map map;
        synchronized()
        {
            map = (Map).get(clazz);
            if(map == null)
            {
                introspectClassInternal(clazz);
                map = (Map).get(clazz);
            }
        }
        return map;
    }

    
Returns the number of introspected methods/properties that should be available via the TemplateHashModel interface. Affected by the setMethodsShadowItems(boolean) and setExposureLevel(int) settings.
    int keyCount(Class clazz)
    {
        Map map = getClassKeyMap(clazz);
        int count = map.size();
        if (map.containsKey())
            count--;
        if (map.containsKey())
            count--;
        if (map.containsKey())
            count--;
        return count;
    }

    
Returns the Set of names of introspected methods/properties that should be available via the TemplateHashModel interface. Affected by the setMethodsShadowItems(boolean) and setExposureLevel(int) settings.
    Set keySet(Class clazz)
    {
        Set set = new HashSet(getClassKeyMap(clazz).keySet());
        set.remove();
        set.remove();
        set.remove();
        return set;
    }
    
    
Populates a map with property and method descriptors for a specified class. If any property or method descriptors specifies a read method that is not accessible, replaces it with appropriate accessible method from a superclass or interface.
    private Map populateClassMap(Class clazz)
    {
        // Populate first from bean info
        Map map = populateClassMapWithBeanInfo(clazz);
        // Next add constructors
        try
        {
            Constructor[] ctors = clazz.getConstructors();
            if(ctors.length == 1)
            {
                Constructor ctor = ctors[0];
                map.put(new SimpleMemberModel(ctorctor.getParameterTypes()));
            }
            else if(ctors.length > 1)
            {
                MethodMap ctorMap = new MethodMap("<init>"this);
                for (int i = 0; i < ctors.lengthi++)
                {
                    ctorMap.addMember(ctors[i]);
                }
                map.put(ctorMap);
            }
        }
        catch(SecurityException e)
        {
            .warn("Canont discover constructors for class " + 
                    clazz.getName(), e);
        }
        switch(map.size())
        {
            case 0:
            {
                map = .;
                break
            }
            case 1:
            {
                Map.Entry e = (Map.Entry)map.entrySet().iterator().next();
                map = Collections12.singletonMap(e.getKey(), e.getValue());
                break;
            }
        }
        return map;
    }
    private Map populateClassMapWithBeanInfo(Class clazz)
    {
        Map classMap = new HashMap();
        if()
        {
            Field[] fields = clazz.getFields();
            for (int i = 0; i < fields.lengthi++)
            {
                Field field = fields[i];
                if((field.getModifiers() & .) == 0)
                {
                    classMap.put(field.getName(), field);
                }
            }
        }
        Map accessibleMethods = discoverAccessibleMethods(clazz);
        Method genericGet = getFirstAccessibleMethod(
                .accessibleMethods);
        if(genericGet == null)
        {
            genericGet = getFirstAccessibleMethod(
                    .accessibleMethods);
        }
        if(genericGet != null)
        {
            classMap.put(genericGet);
        }
        if( == )
        {
            return classMap;
        }
        
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
            PropertyDescriptor[] pda = beanInfo.getPropertyDescriptors();
            MethodDescriptor[] mda = beanInfo.getMethodDescriptors();
            for(int i = pda.length - 1; i >= 0; --i) {
                populateClassMapWithPropertyDescriptor(
                        pda[i], clazzaccessibleMethods,
                        classMap);
            }
            if( < )
            {
                MethodAppearanceDecision decision = new MethodAppearanceDecision();  
                for(int i = mda.length - 1; i >= 0; --i)
                {
                    MethodDescriptor md = mda[i];
                    Method publicMethod = getAccessibleMethod(
                            md.getMethod(), accessibleMethods);
                    if(publicMethod != null && isSafeMethod(publicMethod))
                    {
                        decision.setDefaults(publicMethod);
                        finetuneMethodAppearance(clazzpublicMethoddecision);
                        
                        PropertyDescriptor propDesc = decision.getExposeAsProperty();
                        if (propDesc != null
                                && !(classMap.get(propDesc.getName())
                                        instanceof PropertyDescriptor))
                        {
                            populateClassMapWithPropertyDescriptor(
                                    propDescclazzaccessibleMethods,
                                    classMap);
                        }
                        
                        String methodKey = decision.getExposeMethodAs();
                        if (methodKey != null)
                        {
                            Object previous = classMap.get(methodKey);
                            if(previous instanceof Method)
                            {
                                // Overloaded method - replace method with a method map
                                MethodMap methodMap = new MethodMap(methodKeythis);
                                methodMap.addMember((Method)previous);
                                methodMap.addMember(publicMethod);
                                classMap.put(methodKeymethodMap);
                                // remove parameter type information
                                getArgTypes(classMap).remove(previous);
                            }
                            else if(previous instanceof MethodMap)
                            {
                                // Already overloaded method - add new overload
                                ((MethodMap)previous).addMember(publicMethod);
                            }
                            else if (decision.getMethodShadowsProperty()
                                    || !(previous instanceof PropertyDescriptor))
                            {
                                // Simple method (this far)
                                classMap.put(methodKeypublicMethod);
                                getArgTypes(classMap).put(publicMethod
                                        publicMethod.getParameterTypes());
                            }
                        }
                    }
                }
            }
            return classMap;
        }
        catch(IntrospectionException e) {
            .warn("Couldn't properly perform introspection for class " + 
                    clazze);
            return new HashMap();
        }
    }
            Class clazzMap accessibleMethodsMap classMap) {
        if(pd instanceof IndexedPropertyDescriptor) {
            IndexedPropertyDescriptor ipd = 
                (IndexedPropertyDescriptor)pd;
            Method readMethod = ipd.getIndexedReadMethod();
            Method publicReadMethod = getAccessibleMethod(readMethod
                    accessibleMethods);
            if(publicReadMethod != null && isSafeMethod(publicReadMethod)) {
                try {
                    if(readMethod != publicReadMethod) {
                        ipd = new IndexedPropertyDescriptor(
                                ipd.getName(), ipd.getReadMethod(), 
                                ipd.getWriteMethod(), publicReadMethod
                                ipd.getIndexedWriteMethod());
                    }
                    classMap.put(ipd.getName(), ipd);
                    getArgTypes(classMap).put(publicReadMethod
                            publicReadMethod.getParameterTypes());
                }
                catch(IntrospectionException e) {
                    .warn("Failed creating a publicly-accessible " +
                            "property descriptor for " + clazz.getName() + 
                            " indexed property " + pd.getName() + 
                            ", read method " + publicReadMethod + 
                            ", write method " + ipd.getIndexedWriteMethod(), 
                            e);
                }
            }
        }
        else {
            Method readMethod = pd.getReadMethod();
            Method publicReadMethod = getAccessibleMethod(readMethodaccessibleMethods);
            if(publicReadMethod != null && isSafeMethod(publicReadMethod)) {
                try {
                    if(readMethod != publicReadMethod) {
                        pd = new PropertyDescriptor(pd.getName(), 
                                publicReadMethodpd.getWriteMethod());
                        pd.setReadMethod(publicReadMethod);
                    }
                    classMap.put(pd.getName(), pd);
                }
                catch(IntrospectionException e) {
                    .warn("Failed creating a publicly-accessible " +
                            "property descriptor for " + clazz.getName() + 
                            " property " + pd.getName() + ", read method " + 
                            publicReadMethod + ", write method " + 
                            pd.getWriteMethod(), e);
                }
            }
        }
    }
    
    
Experimental method; subject to change! Override this to tweak certain aspects of how methods appear in the data-model. BeansWrapper will pass in all Java methods here that it intends to expose in the data-model as methods (so you can do obj.foo() in the template). By default this method does nothing. By overriding it you can do the following tweaks:

Note that you can expose a Java method both as a method and as a JavaBean property on the same time, however you have to chose different names for them to prevent shadowing.

Parameters:
decision Stores how the parameter method will be exposed in the data-model after finetuneMethodAppearance(java.lang.Class,java.lang.reflect.Method,freemarker.ext.beans.BeansWrapper.MethodAppearanceDecision) returns. This is initialized so that it reflects the default behavior of BeansWrapper.
    protected void finetuneMethodAppearance(
            Class clazzMethod mMethodAppearanceDecision decision) {
        // left everything on its default; do nothing
    }
    private static Map getArgTypes(Map classMap) {
        Map argTypes = (Map)classMap.get();
        if(argTypes == null) {
            argTypes = new HashMap();
            classMap.put(argTypes);
        }
        return argTypes;
    }
    
    static Class[] getArgTypes(Map classMapAccessibleObject methodOrCtor) {
        return (Class[])((Map)classMap.get()).get(methodOrCtor);
    }
    private static Method getFirstAccessibleMethod(MethodSignature sigMap accessibles)
    {
        List l = (List)accessibles.get(sig);
        if(l == null || l.isEmpty()) {
            return null;
        }
        return (Method)l.iterator().next();
    }
    private static Method getAccessibleMethod(Method mMap accessibles)
    {
        if(m == null) {
            return null;
        }
        MethodSignature sig = new MethodSignature(m);
        List