📄 beanswrapper.java
字号:
/*
* 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.beans.BeanInfo;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.StringTokenizer;
import freemarker.ext.util.ModelCache;
import freemarker.ext.util.ModelFactory;
import freemarker.ext.util.WrapperTemplateModel;
import freemarker.log.Logger;
import freemarker.template.AdapterTemplateModel;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateCollectionModel;
import freemarker.template.TemplateDateModel;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelAdapter;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateNumberModel;
import freemarker.template.TemplateScalarModel;
import freemarker.template.TemplateSequenceModel;
import freemarker.template.utility.ClassUtil;
import freemarker.template.utility.Collections12;
import freemarker.template.utility.SecurityUtilities;
/**
* Utility class that provides generic services to reflection classes.
* It handles all polymorphism issues in the {@link #wrap(Object)} and {@link #unwrap(TemplateModel)} methods.
* @author Attila Szegedi
* @version $Id: BeansWrapper.java,v 1.91.2.6 2006/04/19 16:18:05 szegedia Exp $
*/
public class BeansWrapper implements ObjectWrapper
{
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 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 SET_CLASS = Set.class;
private static final Class STRING_CLASS = String.class;
private static final Class TEMPLATE_MODEL_CLASS = TemplateModel.class;
// 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 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();
/**
* 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 ModelCache modelCache = new ModelCache(this);
private final BooleanModel FALSE = new BooleanModel(Boolean.FALSE, this);
private final BooleanModel TRUE = new BooleanModel(Boolean.TRUE, 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 {@link #setMethodsShadowItems(boolean)} with <tt>false</tt> value to
* speed up map item retrieval.
*/
public static final int EXPOSE_NOTHING = 3;
private int exposureLevel = EXPOSE_SAFE;
private TemplateModel nullModel = null;
private boolean methodsShadowItems = true;
private int defaultDateType = TemplateDateModel.UNKNOWN;
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
* {@link #EXPOSE_SAFE} method exposure level, and will not cache
* model instances.
*/
public BeansWrapper()
{
}
/**
* @see #setStrict(boolean)
*/
public boolean isStrict() {
return strict;
}
/**
* Specifies if an attempt to read a bean property that doesn't exist in the
* wrapped object should throw an {@link InvalidPropertyException}.
*
* <p>If this property is <tt>false</tt> (the default) then an attempt to read
* a missing bean property is the same as reading an existing bean property whose
* value is <tt>null</tt>. The template can't tell the difference, and thus always
* can use <tt>?default('something')</tt> and <tt>?exists</tt> and similar built-ins
* to handle the situation.
*
* <p>If this property is <tt>true</tt> then an attempt to read a bean propertly in
* the template (like <tt>myBean.aProperty</tt>) that doesn't exist in the bean
* object (as opposed to just holding <tt>null</tt> value) will cause
* {@link InvalidPropertyException}, which can't be suppressed in the template
* (not even with <tt>myBean.noSuchProperty?default('something')</tt>). This way
* <tt>?default('something')</tt> and <tt>?exists</tt> and similar built-ins can be used to
* handle existing properties whose value is <tt>null</tt>, 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 = 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.
* @param outerIdentity the aggregate ObjectWrapper
*/
public void setOuterIdentity(ObjectWrapper outerIdentity)
{
this.outerIdentity = outerIdentity;
}
/**
* By default returns <tt>this</tt>.
* @see #setOuterIdentity(ObjectWrapper)
*/
public ObjectWrapper getOuterIdentity()
{
return outerIdentity;
}
/**
* By default the BeansWrapper wraps classes implementing
* java.util.Map using {@link MapModel}. Setting this flag will
* cause it to use a {@link SimpleMapModel} instead. The biggest
* difference is that when using a {@link SimpleMapModel}, the
* map will be visible as <code>TemplateHashModelEx</code>,
* and the subvariables will be the content of the map,
* without the other methods and properties of the map object.
* @param simpleMapWrapper enable simple map wrapping
*/
public void setSimpleMapWrapper(boolean simpleMapWrapper)
{
this.simpleMapWrapper = simpleMapWrapper;
}
public boolean isSimpleMapWrapper()
{
return simpleMapWrapper;
}
/**
* Sets the method exposure level. By default, set to <code>EXPOSE_SAFE</code>.
* @param exposureLevel can be any of the <code>EXPOSE_xxx</code>
* constants.
*/
public void setExposureLevel(int exposureLevel)
{
if(exposureLevel < EXPOSE_ALL || exposureLevel > EXPOSE_NOTHING)
{
throw new IllegalArgumentException("Illegal exposure level " + exposureLevel);
}
this.exposureLevel = exposureLevel;
}
int getExposureLevel()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -