Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Copyright 2014 Attila Szegedi, Daniel Dekany, Jonathan Revusky
    * 
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    * 
    * http://www.apache.org/licenses/LICENSE-2.0
    * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package freemarker.ext.beans;
  
  import java.util.Date;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
freemarker.template.ObjectWrapper that is able to expose the Java API of arbitrary Java objects. This is also the superclass of freemarker.template.DefaultObjectWrapper. Note that instances of this class generally should be created with a BeansWrapperBuilder, not with its public constructors.

As of 2.3.22, using BeansWrapper unextended is not recommended. Instead, freemarker.template.DefaultObjectWrapper with its incompatibleImprovements property set to 2.3.22 (or higher) is the recommended freemarker.template.ObjectWrapper.

This class is only thread-safe after you have finished calling its setter methods, and then safely published it (see JSR 133 and related literature). When used as part of freemarker.template.Configuration, of course it's enough if that was safely published and then left unmodified. Using BeansWrapperBuilder also guarantees thread safety.

  
  public class BeansWrapper implements RichObjectWrapperWriteProtectable
  {
      private static final Logger LOG = Logger.getLogger("freemarker.beans");

    

Deprecated:
Use freemarker.template.ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS instead. It's not a public field anyway.
  
      
      private static final Class ITERABLE_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;
     }
     
     private static final Constructor ENUMS_MODEL_CTOR = enumsModelCtor();
    
    
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;
 
     // -----------------------------------------------------------------------------------------------------------------
     // Introspection cache:
     
     private final Object sharedIntrospectionLock;
    
    
java.lang.Class to class info cache. This object is possibly shared with other BeansWrapper-s!

To write this, always use replaceClassIntrospector(freemarker.ext.beans.ClassIntrospectorBuilder).

When reading this, it's good idea to synchronize on sharedInrospectionLock when it doesn't hurt overall performance. In theory that's not needed, but apps might fail to keep the rules.

 
     private ClassIntrospector classIntrospector;
    
    
java.lang.String class name to StaticModel cache. This object only belongs to a single BeansWrapper. This has to be final as getStaticModels() might returns it any time and then it has to remain a good reference.
 
     private final StaticModels staticModels;
    
    
java.lang.String class name to EnumerationModel cache. This object only belongs to a single BeansWrapper. This has to be final as getStaticModels() might returns it any time and then it has to remain a good reference.
 
     private final ClassBasedModelFactory enumModels;
    
    
Object to wrapped object cache; not used by default. This object only belongs to a single BeansWrapper.
 
     private final ModelCache modelCache;
 
     private final BooleanModel falseModel;
     private final BooleanModel trueModel;
     
     // -----------------------------------------------------------------------------------------------------------------
 
     // Why volatile: In principle it need not be volatile, but we want to catch modification attempts even if the
     // object was published improperly to other threads. After all, the main goal of WriteProtectable is protecting
     // things from buggy user code.
     private volatile boolean writeProtected;
     
     private TemplateModel nullModel = null;
     private int defaultDateType// initialized by PropertyAssignments.apply
     private ObjectWrapper outerIdentity = this;
     private boolean methodsShadowItems = true;
     private boolean simpleMapWrapper;  // initialized by PropertyAssignments.apply
     private boolean strict;  // initialized by PropertyAssignments.apply
     
     private final Version incompatibleImprovements;
    
    
Creates a new instance with the incompatible-improvements-version specified in freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS.

Deprecated:
Use BeansWrapperBuilder or, in rare cases, BeansWrapper(freemarker.template.Version) instead.
 
     public BeansWrapper() {
         // Attention! Don't change fields here, as the instance is possibly already visible to other threads.  
     }
    
    
Use BeansWrapperBuilder instead of the public constructors if possible. The main disadvantage of using the public constructors is that the instances won't share caches. So unless having a private cache is your goal, don't use them. See

Parameters:
incompatibleImprovements Sets which of the non-backward-compatible improvements should be enabled. Not null. This version number is the same as the FreeMarker version number with which the improvements were implemented.

For new projects, it's recommended to set this to the FreeMarker version that's used during the development. For released products that are still actively developed it's a low risk change to increase the 3rd version number further as FreeMarker is updated, but of course you should always check the list of effects below. Increasing the 2nd or 1st version number possibly mean substantial changes with higher risk of breaking the application, but again, see the list of effects below.

The reason it's separate from freemarker.template.Configuration.setIncompatibleImprovements(freemarker.template.Version) is that freemarker.template.ObjectWrapper objects are often shared among multiple freemarker.template.Configuration-s, so the two version numbers are technically independent. But it's recommended to keep those two version numbers the same.

The changes enabled by incompatibleImprovements are:

  • 2.3.0: No changes; this is the starting point, the version used in older projects.

  • 2.3.21 (or higher): Several glitches were fixed in overloaded method selection. This usually just gets rid of errors (like ambiguity exceptions and numerical precision loses due to bad overloaded method choices), still, as in some cases the method chosen can be a different one now (that was the point of the reworking after all), it can mean a change in the behavior of the application. The most important change is that the treatment of null arguments were fixed, as earlier they were only seen applicable to parameters of type Object. Now null-s are seen to be applicable to any non-primitive parameters, and among those the one with the most specific type will be preferred (just like in Java), which is hence never the one with the Object parameter type. For more details about overloaded method selection changes see the version history in the FreeMarker Manual.

Note that the version will be normalized to the lowest version where the same incompatible BeansWrapper improvements were already present, so getIncompatibleImprovements() might returns a lower version than what you have specified.

Since:
2.3.21
 
     public BeansWrapper(Version incompatibleImprovements) {
         this(new BeansWrapperConfiguration(incompatibleImprovements) {}, false);
         // Attention! Don't don anything here, as the instance is possibly already visible to other threads through the
         // model factory callbacks.
     }
     
     private static volatile boolean ftmaDeprecationWarnLogged;
    
    
Same as BeansWrapper(freemarker.ext.beans.BeansWrapperConfiguration,boolean,boolean) with true finalizeConstruction argument.

Since:
2.3.21
 
     protected BeansWrapper(BeansWrapperConfiguration bwConfboolean writeProtected) {
         this(bwConfwriteProtectedtrue);
     }
    
    
Initializes the instance based on the the BeansWrapperConfiguration specified.

Parameters:
writeProtected Makes the instance's configuration settings read-only via freemarker.template.utility.WriteProtectable.writeProtect(); this way it can use the shared class introspection cache.
finalizeConstruction Decides if the construction is finalized now, or the caller will do some more adjustments on the instance and then call finalizeConstruction(boolean) itself.
Since:
2.3.22
 
     protected BeansWrapper(BeansWrapperConfiguration bwConfboolean writeProtectedboolean finalizeConstruction) {
         // Backward-compatibility hack for "finetuneMethodAppearance" overrides to work:
         if (bwConf.getMethodAppearanceFineTuner() == null) {
             Class thisClass = this.getClass();
             boolean overridden = false;
             boolean testFailed = false;
             try {
                 while (!overridden
                         && thisClass != DefaultObjectWrapper.class
                         && thisClass != BeansWrapper.class
                         && thisClass != SimpleObjectWrapper.class) {
                     try {
                         thisClass.getDeclaredMethod("finetuneMethodAppearance",
                                 new Class[] { Class.classMethod.classMethodAppearanceDecision.class });
                         overridden = true;
                     } catch (NoSuchMethodException e) {
                         thisClass = thisClass.getSuperclass();
                     }
                 }
             } catch (Throwable e) {
                 // The security manager sometimes doesn't allow this
                 .info("Failed to check if finetuneMethodAppearance is overidden in " + thisClass.getName()
                         + "; acting like if it was, but this way it won't utilize the shared class introspection "
                         + "cache.",
                         e);
                 overridden = true;
                 testFailed = true;
             }
             if (overridden) {
                 if (!testFailed && !) {
                     .warn("Overriding " + BeansWrapper.class.getName() + ".finetuneMethodAppearance is deprecated "
                             + "and will be banned sometimes in the future. Use setMethodAppearanceFineTuner instead.");
                      = true;
                 }
                 bwConf = (BeansWrapperConfigurationbwConf.clone(false);
                 bwConf.setMethodAppearanceFineTuner(new MethodAppearanceFineTuner() {
 
                     public void process(
                             MethodAppearanceDecisionInput inMethodAppearanceDecision out) {
                         BeansWrapper.this.finetuneMethodAppearance(in.getContainingClass(), in.getMethod(), out);
                     }
                     
                 });
             }
         }
         
         this. = bwConf.getIncompatibleImprovements();  // normalized
         
          = bwConf.isSimpleMapWrapper();
          = bwConf.getDefaultDateType();
          = bwConf.getOuterIdentity() != null ? bwConf.getOuterIdentity() : this;
          = bwConf.isStrict();
         
         if (!writeProtected) {
             // As this is not a read-only BeansWrapper, the classIntrospector will be possibly replaced for a few times,
             // but we need to use the same sharedInrospectionLock forever, because that's what the model factories
             // synchronize on, even during the classIntrospector is being replaced.
              = new Object();
              = new ClassIntrospector(bwConf.classIntrospectorFactory);
         } else {
             // As this is a read-only BeansWrapper, the classIntrospector is never replaced, and since it's shared by
             // other BeansWrapper instances, we use the lock belonging to the shared ClassIntrospector.
              = bwConf.classIntrospectorFactory.build();
         }
         
          = new BooleanModel(.this);
          = new BooleanModel(.this);
         
          = new StaticModels(BeansWrapper.this);
          = createEnumModels(BeansWrapper.this);
          = new BeansModelCache(BeansWrapper.this);
         setUseCache(bwConf.getUseModelCache());
 
         finalizeConstruction(writeProtected);
     }

    
Meant to be called after BeansWrapper(freemarker.ext.beans.BeansWrapperConfiguration,boolean,boolean) when its last argument was false; makes the instance read-only if necessary, then registers the model factories in the class introspector. No further changes should be done after calling this, if writeProtected was true.

Since:
2.3.22
 
     protected void finalizeConstruction(boolean writeProtected) {
         if (writeProtected) {
             writeProtect();
         }
         
         // Attention! At this point, the BeansWrapper must be fully initialized, as when the model factories are
         // registered below, the BeansWrapper can immediately get concurrent callbacks. That those other threads will
         // see consistent image of the BeansWrapper is ensured that callbacks are always sync-ed on
         // classIntrospector.sharedLock, and so is classIntrospector.registerModelFactory(...).
         
         registerModelFactories();
     }
    
    
Makes the configuration properties (settings) of this BeansWrapper object read-only. As changing them after the object has become visible to multiple threads leads to undefined behavior, it's recommended to call this when you have finished configuring the object.

Consider using BeansWrapperBuilder instead, which gives an instance that's already write protected and also uses some shared caches/pools.

Since:
2.3.21
 
     public void writeProtect() {
          = true;
     }

    

Since:
2.3.21
 
     public boolean isWriteProtected() {
         return ;
     }
     
         return ;
     }
    
    
If this object is already read-only according to freemarker.template.utility.WriteProtectable, throws java.lang.IllegalStateException, otherwise does nothing.

Since:
2.3.21
 
     protected void checkModifiable() {
         if (throw new IllegalStateException(
                 "Can't modify the " + this.getClass().getName() + " object, as it was write protected.");
     }

    
 
     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 you are doing.

 
     public void setStrict(boolean strict) {
         checkModifiable();
     	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)
     {
         checkModifiable();
         this. = outerIdentity;
     }

    
By default returns this.

 
     public ObjectWrapper getOuterIdentity()
     {
         return ;
     }

    
When set to true, the keys in java.util.Map-s won't mix with the method names when looking at them from templates. The default is false for backward-compatibility, but is not recommended.

When this is false, myMap.foo or myMap['foo'] either returns the method foo, or calls Map.get("foo"). If both exists (the method and the java.util.Map key), one will hide the other, depending on the isMethodsShadowItems(), which default to true (the method wins). Some frameworks use this so that you can call myMap.get(nonStringKey) from templates [*], but it comes on the cost of polluting the key-set with the method names, and risking methods accidentally hiding java.util.Map entries (or the other way around). Thus, this setup is not recommended. (Technical note: java.util.Map-s will be wrapped into MapModel in this case.)

When this is true, myMap.foo or myMap['foo'] always calls Map.get("foo"). The methods of the java.util.Map object aren't visible from templates in this case. This, however, spoils the myMap.get(nonStringKey) workaround. But now you can use myMap(nonStringKey) instead, that is, you can use the map itself as the get method. (Technical note: java.util.Map-s will be wrapped into SimpleMapModel in this case.)

*: For historical reasons, FreeMarker 2.3.X doesn't support non-string keys with the [] operator, hence the workarounds. This will be likely fixed in FreeMarker 2.4.0. Also note that the method- and the "field"-namespaces aren't separate in FreeMarker, hence myMap.get can return the get method.

 
     public void setSimpleMapWrapper(boolean simpleMapWrapper)
     {
         checkModifiable();
         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 ;
     }
 
     // I have commented this out, as it won't be in 2.3.20 yet.
     /*
     /**
      * Tells which non-backward-compatible overloaded method selection fixes to apply;
      * see {@link #setOverloadedMethodSelection(Version)}.
      * /
     public Version getOverloadedMethodSelection() {
         return overloadedMethodSelection;
     }
 
     /**
      * Sets which non-backward-compatible overloaded method selection fixes to apply.
      * This has similar logic as {@link Configuration#setIncompatibleImprovements(Version)},
      * but only applies to this aspect.
      * 
      * Currently significant values:
      * <ul>
      *   <li>2.3.21: Completetlly rewritten overloaded method selection, fixes several issues with the old one.</li>
      * </ul>
      * /
     public void setOverloadedMethodSelection(Version version) {
         overloadedMethodSelection = version;
     }
     */
    
    
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)
     {
         checkModifiable();
      
         if (.getExposureLevel() != exposureLevel) {
             pa.setExposureLevel(exposureLevel);
             replaceClassIntrospector(pa);
         }
     }
    
    

Since:
2.3.21
 
     public int getExposureLevel()
     {
         return .getExposureLevel();
     }
    
    
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)
     {
         checkModifiable();
         
         if (.getExposeFields() != exposeFields) {
             pa.setExposeFields(exposeFields);
             replaceClassIntrospector(pa);
         }
     }
    
    
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 .getExposeFields();
     }
     
     }

    
Used to tweak certain aspects of how methods appear in the data-model; see MethodAppearanceFineTuner for more.
 
     public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner methodAppearanceFineTuner) {
         checkModifiable();
         
         if (.getMethodAppearanceFineTuner() != methodAppearanceFineTuner) {
             pa.setMethodAppearanceFineTuner(methodAppearanceFineTuner);
             replaceClassIntrospector(pa);
         }
     }
 
         return .getMethodSorter();
     }
 
     void setMethodSorter(MethodSorter methodSorter) {
         checkModifiable();
         
         if (.getMethodSorter() != methodSorter) {
             pa.setMethodSorter(methodSorter);
             replaceClassIntrospector(pa);
         }
     }
    
    
Tells if this instance acts like if its class introspection cache is sharable with other BeansWrapper-s. A restricted cache denies certain too "antisocial" operations, like clearClassIntrospecitonCache(). The value depends on how the instance was created; with a public constructor (then this is false), or with BeansWrapperBuilder (then it's true). Note that in the last case it's possible that the introspection cache will not be actually shared because there's no one to share with, but this will true even then.

Since:
2.3.21
 
     public boolean isClassIntrospectionCacheRestricted() {
     }
    
    
Replaces the value of classIntrospector, but first it unregisters the model factories in the old classIntrospector.
 
     private void replaceClassIntrospector(ClassIntrospectorBuilder pa) {
         checkModifiable();
         
         final ClassIntrospector newCI = new ClassIntrospector(pa);
         final ClassIntrospector oldCI;
         
         // In principle this need not be synchronized, but as apps might publish the configuration improperly, or
         // even modify the wrapper after publishing. This doesn't give 100% protection from those violations,
         // as classIntrospector reading aren't everywhere synchronized for performance reasons. It still decreases the
         // chance of accidents, because some ops on classIntrospector are synchronized, and because it will at least
         // push the new value into the common shared memory.
         synchronized () {
             oldCI = ;
             if (oldCI != null) {
                 // Note that after unregistering the model factory might still gets some callback from the old
                 // classIntrospector
                 if ( != null) {
                     oldCI.unregisterModelFactory();
                     .clearCache();
                 }
                 if ( != null) {
                     oldCI.unregisterModelFactory();
                     .clearCache();
                 }
                 if ( != null) {
                     oldCI.unregisterModelFactory();
                     .clearCache();
                 }
                 if ( != null) {
                     .clearMemberCache();
                 }
                 if ( != null) {
                     .clearMemberCache();
                 }
             }
             
              = newCI;
             
             registerModelFactories();
         }
     }
 
     private void registerModelFactories() {
         if ( != null) {
         }
         if ( != null) {
         }
         if ( != null) {
         }
     }

    
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 void setMethodsShadowItems(boolean methodsShadowItems)
     {
         // This sync is here as this method was originally synchronized, but was never truly thread-safe, so I don't
         // want to advertise it in the javadoc, nor I wanted to break any apps that work because of this accidentally.
         synchronized (this) {
             checkModifiable();
             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 void setDefaultDateType(int defaultDateType) {
         // This sync is here as this method was originally synchronized, but was never truly thread-safe, so I don't
         // want to advertise it in the javadoc, nor I wanted to break any apps that work because of this accidentally.
         synchronized (this) {
             checkModifiable();
             
             this. = defaultDateType;
         }
     }

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

Returns:
the default date type
 
     public int getDefaultDateType() {
         return ;
     }
    
    
Sets whether this wrapper caches the freemarker.template.TemplateModel-s created for the Java objects that has wrapped with this object wrapper. 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 any time).
 
     public void setUseCache(boolean useCache)
     {
         checkModifiable();
         .setUseCache(useCache);
     }

    

Since:
2.3.21
 
     public boolean getUseCache()
     {
         return .getUseCache();
     }
    
    
Sets the null model. This model is returned from the wrap(java.lang.Object) method whenever the wrapped object is null. It defaults to null, which is dealt with quite strictly on engine level, however you can substitute an arbitrary (perhaps more lenient) model, like an empty string. For proper working, the nullModel should be an freemarker.template.AdapterTemplateModel that returns null for freemarker.template.AdapterTemplateModel.getAdaptedObject(java.lang.Class).

Deprecated:
Changing the null model can cause a lot of confusion; don't do it.
 
     public void setNullModel(TemplateModel nullModel)
     {
         checkModifiable();
         this. = nullModel;
     }
    
    
Returns the version given with BeansWrapper(freemarker.template.Version), normalized to the lowest version where a change has occurred. Thus, this is not necessarily the same version than that was given to the constructor.

Since:
2.3.21
 
     public Version getIncompatibleImprovements() {
         return ;
     }
     
     boolean is2321Bugfixed() {
         return is2321Bugfixed(getIncompatibleImprovements());
     }
 
     static boolean is2321Bugfixed(Version version) {
         return version.intValue() >= .;
     }
    
    
Returns the lowest version number that is equivalent with the parameter version.

Since:
2.3.21
 
     protected static Version normalizeIncompatibleImprovementsVersion(Version incompatibleImprovements) {
         _TemplateAPI.checkVersionNotNullAndSupported(incompatibleImprovements);
         if (incompatibleImprovements.intValue() < .) {
             throw new IllegalArgumentException("Version must be at least 2.3.0.");
         }
         return is2321Bugfixed(incompatibleImprovements) ? . : .;
     }
    
    
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.

Deprecated:
Use BeansWrapperBuilder instead. The instance returned here is not read-only, so it's dangerous to use.
 
     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 == nullreturn ;
         return .getInstance(object);
     }
    
    
Wraps a Java method so that it can be called from templates, without wrapping its parent ("this") object. The result is almost the same as that you would get by wrapping the parent object then getting the method from the resulting freemarker.template.TemplateHashModel by name. Except, if the wrapped method is overloaded, with this method you explicitly select a an overload, while otherwise you would get a freemarker.template.TemplateMethodModelEx that selects an overload each time it's called based on the argument values.

Parameters:
object The object whose method will be called, or null if method is a static method. This object will be used "as is", like without unwrapping it if it's a freemarker.template.TemplateModelAdapter.
method The method to call, which must be an (inherited) member of the class of object, as described by java.lang.reflect.Method.invoke(java.lang.Object,java.lang.Object[])
Since:
2.3.22
 
     public TemplateMethodModelEx wrap(Object objectMethod method) {
         return new SimpleMethodModel(objectmethodmethod.getParameterTypes(), this);
     }
    
    

Since:
2.3.22
 
     public TemplateHashModel wrapAsAPI(Object objthrows TemplateModelException {
         return new APIModel(objthis);
     }

    

Deprecated:
override getModelFactory(java.lang.Class) instead. Using this method will now bypass wrapper caching (if it's enabled) and always result in creation of a new wrapper. This method will be removed in 2.4
Parameters:
object The object to wrap
factory The factory that wraps the object
 
     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(modelObject.class);
     }

    
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 target classes - all Java built-in primitives, primitive wrappers, numbers, dates, sets, lists, maps, and native arrays.

Parameters:
model the model to unwrap
targetClass the class of the unwrapped result; Object.class of we don't know what the expected type is.
Returns:
the unwrapped result of the desired class
Throws:
freemarker.template.TemplateModelException if an attempted unwrapping fails.
See also:
tryUnwrapTo(freemarker.template.TemplateModel,java.lang.Class)
 
     public Object unwrap(TemplateModel modelClass targetClass
     throws TemplateModelException
     {
         final Object obj = tryUnwrapTo(modeltargetClass);
           throw new TemplateModelException("Can not unwrap model of type " + 
               model.getClass().getName() + " to type " + targetClass.getName());
         }
         return obj;
     }

    

Since:
2.3.22
 
     public Object tryUnwrapTo(TemplateModel modelClass targetClassthrows TemplateModelException
     {
         return tryUnwrapTo(modeltargetClass, 0);
     }
    
    

Parameters:
typeFlags Used when unwrapping for overloaded methods and so the targetClass is possibly too generic. Must be 0 when unwrapping parameter values for non-overloaded methods, also if is2321Bugfixed() is false.
Returns:
freemarker.template.ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS or the unwrapped object.
    Object tryUnwrapTo(TemplateModel modelClass targetClassint typeFlags
    {
        Object res = tryUnwrapTo(modeltargetClasstypeFlagsnull);
        if ((typeFlags & .) != 0
                && res instanceof Number) {
            return OverloadedNumberUtil.addFallbackType((NumberrestypeFlags);
        } else {
            return res;
        }
    }

    
See .tryUnwrap(TemplateModel, Class, int, boolean).
    private Object tryUnwrapTo(final TemplateModel modelClass targetClassfinal int typeFlagsfinal Map recursionStops
    throws TemplateModelException {
        if(model == null || model == ) {
            return null;
        }
        
        final boolean is2321Bugfixed = is2321Bugfixed();
        
        if (is2321Bugfixed && targetClass.isPrimitive()) {
            targetClass = ClassUtil.primitiveClassToBoxingClass(targetClass);            
        }
        
        // This is for transparent interop with other wrappers (and ourselves)
        // Passing the targetClass 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 wrapped = ((AdapterTemplateModel)model).getAdaptedObject(
                    targetClass);
            if (targetClass == Object.class || targetClass.isInstance(wrapped)) {
                return wrapped;
            }
            
            // Attempt numeric conversion: 
            if (targetClass != Object.class && (wrapped instanceof Number && ClassUtil.isNumerical(targetClass))) {
                Number number = forceUnwrappedNumberToType((NumberwrappedtargetClassis2321Bugfixed);
                if(number != nullreturn number;
            }
        }
        
        if(model instanceof WrapperTemplateModel) {
            Object wrapped = ((WrapperTemplateModel)model).getWrappedObject();
            if (targetClass == Object.class || targetClass.isInstance(wrapped)) {
                return wrapped;
            }
            
            // Attempt numeric conversion: 
            if(targetClass != Object.class && (wrapped instanceof Number && ClassUtil.isNumerical(targetClass))) {
                Number number = forceUnwrappedNumberToType((NumberwrappedtargetClassis2321Bugfixed);
                if(number != null) {
                    return number;
                }
            }
        }
        
        // Translation of generic template models to POJOs. First give priority
        // to various model interfaces based on the targetClass. This helps us
        // select the appropriate interface in multi-interface models when we
        // know what is expected as the return type.
        if (targetClass != Object.class) {
            // Java 5: Also should check for CharSequence at the end
            if(String.class == targetClass) {
                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(ClassUtil.isNumerical(targetClass)) {
                if(model instanceof TemplateNumberModel) {
                    Number number = forceUnwrappedNumberToType(
                            ((TemplateNumberModel)model).getAsNumber(), targetClassis2321Bugfixed);
                    if(number != null) {
                        return number;
                    }
                }
            }
            
            if(boolean.class == targetClass || Boolean.class == targetClass) {
                if(model instanceof TemplateBooleanModel) {
                    return Boolean.valueOf(((TemplateBooleanModelmodel).getAsBoolean());
                }
                // Boolean is final, no other conversion will work
                return .;
            }
    
            if(Map.class == targetClass) {
                if(model instanceof TemplateHashModel) {
                    return new HashAdapter((TemplateHashModel)modelthis);
                }
            }
            
            if(List.class == targetClass) {
                if(model instanceof TemplateSequenceModel) {
                    return new SequenceAdapter((TemplateSequenceModel)modelthis);
                }
            }
            
            if(Set.class == targetClass) {
                if(model instanceof TemplateCollectionModel) {
                    return new SetAdapter((TemplateCollectionModel)modelthis);
                }
            }
            
            if(Collection.class == targetClass ||  == targetClass) {
                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(targetClass.isArray()) {
                if(model instanceof TemplateSequenceModel) {
                    return unwrapSequenceToArray((TemplateSequenceModelmodeltargetClasstruerecursionStops);
                }
                // array classes are final, no other conversion will work
                return .;
            }
            
            // Allow one-char strings to be coerced to characters
            if(char.class == targetClass || targetClass == Character.class) {
                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(Date.class.isAssignableFrom(targetClass) && model instanceof TemplateDateModel) {
                Date date = ((TemplateDateModel)model).getAsDate();
                if(targetClass.isInstance(date)) {
                    return date;
                }
            }
        }  //  End: if (targetClass != Object.class)
        
        // Since the targetClass was of no help initially, now we use
        // a quite arbitrary order in which we walk through the TemplateModel subinterfaces, and unwrapp them to
        // their "natural" Java correspondent. We still try exclude unwrappings that won't fit the target parameter
        // type(s). This is mostly important because of multi-typed FTL values that could be unwrapped on multiple ways.
        int itf = typeFlags// Iteration's Type Flags. Should be always 0 for non-overloaded and when !is2321Bugfixed.
        // If itf != 0, we possibly execute the following loop body at twice: once with utilizing itf, and if it has not
        // returned, once more with itf == 0. Otherwise we execute this once with itf == 0.
        do {
            if ((itf == 0 || (itf & .) != 0)
                    && model instanceof TemplateNumberModel) {
                Number number = ((TemplateNumberModelmodel).getAsNumber();
                if (itf != 0 || targetClass.isInstance(number)) {
                    return number;
                }
            }
            if ((itf == 0 || (itf & .) != 0)
                    && model instanceof TemplateDateModel) {
                Date date = ((TemplateDateModelmodel).getAsDate();
                if (itf != 0 || targetClass.isInstance(date)) {
                    return date;
                }
            }
            if ((itf == 0 || (itf & (. | .)) != 0)
                    && model instanceof TemplateScalarModel
                    && (itf != 0 || targetClass.isAssignableFrom(String.class))) {
                String strVal = ((TemplateScalarModelmodel).getAsString();
                if (itf == 0 || (itf & .) == 0) {
                    return strVal;
                } else { // TypeFlags.CHAR == 1
                    if (strVal.length() == 1) {
                        if ((itf & .) != 0) {
                            return new CharacterOrString(strVal);
                        } else {
                            return new Character(strVal.charAt(0));
                        }
                    } else if ((itf & .) != 0) {
                        return strVal
                    }
                    // It had to be unwrapped to Character, but the string length wasn't 1 => Fall through
                }
            }
            // Should be earlier than TemplateScalarModel, but we keep it here until FM 2.4 or such
            if ((itf == 0 || (itf & .) != 0)
                    && model instanceof TemplateBooleanModel
                    && (itf != 0 || targetClass.isAssignableFrom(Boolean.class))) {
                return Boolean.valueOf(((TemplateBooleanModelmodel).getAsBoolean());
            }
            if ((itf == 0 || (itf & .) != 0)
                    && model instanceof TemplateHashModel
                    && (itf != 0 || targetClass.isAssignableFrom(HashAdapter.class))) {
                return new HashAdapter((TemplateHashModelmodelthis);
            }
            if ((itf == 0 || (itf & .) != 0)
                    && model instanceof TemplateSequenceModel 
                    && (itf != 0 || targetClass.isAssignableFrom(SequenceAdapter.class))) {
                return new SequenceAdapter((TemplateSequenceModelmodelthis);
            }
            if ((itf == 0 || (itf & .) != 0)
                    && model instanceof TemplateCollectionModel
                    && (itf != 0 || targetClass.isAssignableFrom(SetAdapter.class))) {
                return new SetAdapter((TemplateCollectionModelmodelthis);
            }
            
            // In 2.3.21 bugfixed mode only, List-s are convertible to arrays on invocation time. Only overloaded
            // methods need this. As itf will be 0 in non-bugfixed mode and for non-overloaded method calls, it's
            // enough to check if the TypeFlags.ACCEPTS_ARRAY bit is 1:
            if ((itf & .) != 0
                    && model instanceof TemplateSequenceModel) {
                return new SequenceAdapter((TemplateSequenceModelmodelthis);
            }
            
            if (itf == 0) {
                break;
            }
            itf = 0; // start 2nd iteration
        } while (true);
        // Last ditch effort - is maybe the model itself is an instance of the required type?
        // Note that this will be always true for Object.class targetClass. 
        if (targetClass.isInstance(model)) {
            return model;
        }
        
    }

    

Parameters:
tryOnly If true, if the conversion of an item to the component type isn't possible, the method returns freemarker.template.ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS instead of throwing a freemarker.template.TemplateModelException.
    Object unwrapSequenceToArray(TemplateSequenceModel seqClass arrayClassboolean tryOnlyMap recursionStops)
            throws TemplateModelException {
        if(recursionStops != null) {
            Object retval = recursionStops.get(seq);
            if(retval != null) {
                return retval;
            }
        } else {
            recursionStops = new IdentityHashMap();
        }
        Class componentType = arrayClass.getComponentType();
        Object array = Array.newInstance(componentTypeseq.size());
        recursionStops.put(seqarray);
        try {
            final int size = seq.size();
            for (int i = 0; i < sizei++) {
                final TemplateModel seqItem = seq.get(i);
                Object val = tryUnwrapTo(seqItemcomponentType, 0, recursionStops);
                if(val == .) {
                    if (tryOnly) {
                        return .;
                    } else {
                        throw new _TemplateModelException(new Object[] {
                                "Failed to convert ",  new _DelayedFTLTypeDescription(seq),
                                " object to "new _DelayedShortClassName(array.getClass()),
                                ": Problematic sequence item at index "new Integer(i) ," with value type: ",
                                new _DelayedFTLTypeDescription(seqItem)});
                    }
                    
                }
                Array.set(arrayival);
            }
        } finally {
            recursionStops.remove(seq);
        }
        return array;
    }
    
    Object listToArray(List listClass arrayClassMap recursionStops)
            throws TemplateModelException {
        if (list instanceof SequenceAdapter) {
            return unwrapSequenceToArray(
                    ((SequenceAdapterlist).getTemplateSequenceModel(),
                    arrayClassfalse,
                    recursionStops);
        }
        
        if(recursionStops != null) {
            Object retval = recursionStops.get(list);
            if(retval != null) {
                return retval;
            }
        } else {
            recursionStops = new IdentityHashMap();
        }
        Class componentType = arrayClass.getComponentType();
        Object array = Array.newInstance(componentTypelist.size());
        recursionStops.put(listarray);
        try {
            boolean isComponentTypeExamined = false;
            boolean isComponentTypeNumerical = false;  // will be filled on demand
            boolean isComponentTypeList = false;  // will be filled on demand
            int i = 0;
            for (Iterator it = list.iterator(); it.hasNext();) {
                Object listItem = it.next();
                if (listItem != null && !componentType.isInstance(listItem)) {
                    // Type conversion is needed. If we can't do it, we just let it fail at Array.set later.
                    if (!isComponentTypeExamined) {
                        isComponentTypeNumerical = ClassUtil.isNumerical(componentType);
                        isComponentTypeList = List.class.isAssignableFrom(componentType);
                        isComponentTypeExamined = true;
                    }
                    if (isComponentTypeNumerical && listItem instanceof Number) {
                        listItem = forceUnwrappedNumberToType((NumberlistItemcomponentTypetrue);
                    } else if (componentType == String.class && listItem instanceof Character) {
                        listItem = String.valueOf(((CharacterlistItem).charValue());
                    } else if ((componentType == Character.class || componentType == char.class)
                            && listItem instanceof String) {
                        String listItemStr = (StringlistItem;
                        if (listItemStr.length() == 1) {
                            // Java 5: use Character.valueOf
                            listItem = new Character(listItemStr.charAt(0));
                        }
                    } else if (componentType.isArray()) {
                        if (listItem instanceof List) {
                            listItem = listToArray((ListlistItemcomponentTyperecursionStops);
                        } else if (listItem instanceof TemplateSequenceModel) {
                            listItem = unwrapSequenceToArray((TemplateSequenceModellistItemcomponentTypefalserecursionStops);
                        }
                    } else if (isComponentTypeList && listItem.getClass().isArray()) {
                        listItem = arrayToList(listItem);
                    }
                }
                try {
                    Array.set(arrayilistItem);
                } catch (IllegalArgumentException e) {
                    throw new TemplateModelException(
                            "Failed to convert " + ClassUtil.getShortClassNameOfObject(list)
                            + " object to " + ClassUtil.getShortClassNameOfObject(array)
                            + ": Problematic List item at index " + i + " with value type: "
                            + ClassUtil.getShortClassNameOfObject(listItem), e);
                }
                i++;
            }
        } finally {
            recursionStops.remove(list);
        }
        return array;
    }
    
    

Parameters:
array Must be an array (of either a reference or primitive type)
    List arrayToList(Object arraythrows TemplateModelException {
        if (array instanceof Object[]) {
            // Array of any non-primitive type.
            // Note that an array of non-primitive type is always instanceof Object[].
            Object[] objArray = (Object[]) array;
            return objArray.length == 0 ? . : new NonPrimitiveArrayBackedReadOnlyList(objArray);
        } else {
            // Array of any primitive type
            return Array.getLength(array) == 0 ? . : new PrimtiveArrayBackedReadOnlyList(array);
        }
    }

    
Converts a number to the target type aggressively (possibly with overflow or significant loss of precision).

Parameters:
n Non-null
Returns:
null if the conversion has failed.
    static Number forceUnwrappedNumberToType(final Number nfinal Class targetTypefinal boolean bugfixed) {
        // We try to order the conditions by decreasing probability.
        if (targetType == n.getClass()) {
            return n;
        } else if (targetType == int.class || targetType == Integer.class) {
            return n instanceof Integer ? (Integern : new Integer(n.intValue());
        } else if (targetType == long.class || targetType == Long.class) {
            return n instanceof Long ? (Longn : new Long(n.longValue());
        } else if (targetType == double.class || targetType == Double.class) {
            return n instanceof Double ? (Doublen : new Double(n.doubleValue());
        } else if(targetType == BigDecimal.class) {
            if(n instanceof BigDecimal) {
                return n;
            } else if (n instanceof BigInteger) {
                return new BigDecimal((BigIntegern);
            } else if (n instanceof Long) {
                // Because we can't represent long accurately as double
                return BigDecimal.valueOf(n.longValue());
            } else {
                return new BigDecimal(n.doubleValue());
            }
        } else if (targetType == float.class || targetType == Float.class) {
            return n instanceof Float ? (Floatn : new Float(n.floatValue());
        } else if (targetType == byte.class || targetType == Byte.class) {
            return n instanceof Byte ? (Byten : new Byte(n.byteValue());
        } else if (targetType == short.class || targetType == Short.class) {
            return n instanceof Short ? (Shortn : new Short(n.shortValue());
        } else if (targetType == BigInteger.class) {
            if (n instanceof BigInteger) {
                return n;
            } else if (bugfixed) {
                if (n instanceof OverloadedNumberUtil.IntegerBigDecimal) {
                    return ((OverloadedNumberUtil.IntegerBigDecimaln).bigIntegerValue();
                } else if (n instanceof BigDecimal) {
                    return ((BigDecimaln).toBigInteger(); 
                } else {
                    return BigInteger.