http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/_MethodUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/_MethodUtil.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/_MethodUtil.java deleted file mode 100644 index f06f1f9..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/beans/_MethodUtil.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.freemarker.core.model.impl.beans; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.HashSet; -import java.util.Set; - -import org.apache.freemarker.core._DelayedConversionToString; -import org.apache.freemarker.core._DelayedJQuote; -import org.apache.freemarker.core._TemplateModelException; -import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.util.BugException; -import org.apache.freemarker.core.util._ClassUtil; - -/** - * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! - */ -public final class _MethodUtil { - - private _MethodUtil() { - // Not meant to be instantiated - } - - /** - * Determines whether the type given as the 1st argument is convertible to the type given as the 2nd argument - * for method call argument conversion. This follows the rules of the Java reflection-based method call, except - * that since we don't have the value here, a boxed class is never seen as convertible to a primitive type. - * - * @return 0 means {@code false}, non-0 means {@code true}. - * That is, 0 is returned less specificity or incomparable specificity, also when if - * then method was aborted because of {@code ifHigherThan}. - * The absolute value of the returned non-0 number symbolizes how more specific it is: - * <ul> - * <li>1: The two classes are identical</li> - * <li>2: The 1st type is primitive, the 2nd type is the corresponding boxing class</li> - * <li>3: Both classes are numerical, and one is convertible into the other with widening conversion. - * E.g., {@code int} is convertible to {@code long} and {#code double}, hence {@code int} is more - * specific. - * This ignores primitive VS boxed mismatches, except that a boxed class is never seen as - * convertible to a primitive class.</li> - * <li>4: One class is {@code instanceof} of the other, but they aren't identical. - * But unlike in Java, primitive numerical types are {@code instanceof} {@link Number} here.</li> - * </ul> - */ - // TODO Seems that we don't use the full functionality of this anymore, so we could simplify this. See usages. - public static int isMoreOrSameSpecificParameterType(final Class specific, final Class generic, boolean bugfixed, - int ifHigherThan) { - if (ifHigherThan >= 4) return 0; - if (generic.isAssignableFrom(specific)) { - // Identity or widening reference conversion: - return generic == specific ? 1 : 4; - } else { - final boolean specificIsPrim = specific.isPrimitive(); - final boolean genericIsPrim = generic.isPrimitive(); - if (specificIsPrim) { - if (genericIsPrim) { - if (ifHigherThan >= 3) return 0; - return isWideningPrimitiveNumberConversion(specific, generic) ? 3 : 0; - } else { // => specificIsPrim && !genericIsPrim - if (bugfixed) { - final Class specificAsBoxed = _ClassUtil.primitiveClassToBoxingClass(specific); - if (specificAsBoxed == generic) { - // A primitive class is more specific than its boxing class, because it can't store null - return 2; - } else if (generic.isAssignableFrom(specificAsBoxed)) { - // Note: This only occurs if `specific` is a primitive numerical, and `generic == Number` - return 4; - } else if (ifHigherThan >= 3) { - return 0; - } else if (Number.class.isAssignableFrom(specificAsBoxed) - && Number.class.isAssignableFrom(generic)) { - return isWideningBoxedNumberConversion(specificAsBoxed, generic) ? 3 : 0; - } else { - return 0; - } - } else { - return 0; - } - } - } else { // => !specificIsPrim - if (ifHigherThan >= 3) return 0; - if (bugfixed && !genericIsPrim - && Number.class.isAssignableFrom(specific) && Number.class.isAssignableFrom(generic)) { - return isWideningBoxedNumberConversion(specific, generic) ? 3 : 0; - } else { - return 0; - } - } - } // of: !generic.isAssignableFrom(specific) - } - - private static boolean isWideningPrimitiveNumberConversion(final Class source, final Class target) { - if (target == Short.TYPE && (source == Byte.TYPE)) { - return true; - } else if (target == Integer.TYPE && - (source == Short.TYPE || source == Byte.TYPE)) { - return true; - } else if (target == Long.TYPE && - (source == Integer.TYPE || source == Short.TYPE || - source == Byte.TYPE)) { - return true; - } else if (target == Float.TYPE && - (source == Long.TYPE || source == Integer.TYPE || - source == Short.TYPE || source == Byte.TYPE)) { - return true; - } else if (target == Double.TYPE && - (source == Float.TYPE || source == Long.TYPE || - source == Integer.TYPE || source == Short.TYPE || - source == Byte.TYPE)) { - return true; - } else { - return false; - } - } - - private static boolean isWideningBoxedNumberConversion(final Class source, final Class target) { - if (target == Short.class && source == Byte.class) { - return true; - } else if (target == Integer.class && - (source == Short.class || source == Byte.class)) { - return true; - } else if (target == Long.class && - (source == Integer.class || source == Short.class || - source == Byte.class)) { - return true; - } else if (target == Float.class && - (source == Long.class || source == Integer.class || - source == Short.class || source == Byte.class)) { - return true; - } else if (target == Double.class && - (source == Float.class || source == Long.class || - source == Integer.class || source == Short.class || - source == Byte.class)) { - return true; - } else { - return false; - } - } - - /** - * Attention, this doesn't handle primitive classes correctly, nor numerical conversions. - */ - public static Set getAssignables(Class c1, Class c2) { - Set s = new HashSet(); - collectAssignables(c1, c2, s); - return s; - } - - private static void collectAssignables(Class c1, Class c2, Set s) { - if (c1.isAssignableFrom(c2)) { - s.add(c1); - } - Class sc = c1.getSuperclass(); - if (sc != null) { - collectAssignables(sc, c2, s); - } - Class[] itf = c1.getInterfaces(); - for (Class anItf : itf) { - collectAssignables(anItf, c2, s); - } - } - - public static Class[] getParameterTypes(Member member) { - if (member instanceof Method) { - return ((Method) member).getParameterTypes(); - } - if (member instanceof Constructor) { - return ((Constructor) member).getParameterTypes(); - } - throw new IllegalArgumentException("\"member\" must be Method or Constructor"); - } - - public static boolean isVarargs(Member member) { - if (member instanceof Method) { - return ((Method) member).isVarArgs(); - } - if (member instanceof Constructor) { - return ((Constructor) member).isVarArgs(); - } - throw new BugException(); - } - - /** - * Returns a more streamlined method or constructor description than {@code Member.toString()} does. - */ - public static String toString(Member member) { - if (!(member instanceof Method || member instanceof Constructor)) { - throw new IllegalArgumentException("\"member\" must be a Method or Constructor"); - } - - StringBuilder sb = new StringBuilder(); - - if ((member.getModifiers() & Modifier.STATIC) != 0) { - sb.append("static "); - } - - String className = _ClassUtil.getShortClassName(member.getDeclaringClass()); - if (className != null) { - sb.append(className); - sb.append('.'); - } - sb.append(member.getName()); - - sb.append('('); - Class[] paramTypes = _MethodUtil.getParameterTypes(member); - for (int i = 0; i < paramTypes.length; i++) { - if (i != 0) sb.append(", "); - String paramTypeDecl = _ClassUtil.getShortClassName(paramTypes[i]); - if (i == paramTypes.length - 1 && paramTypeDecl.endsWith("[]") && _MethodUtil.isVarargs(member)) { - sb.append(paramTypeDecl.substring(0, paramTypeDecl.length() - 2)); - sb.append("..."); - } else { - sb.append(paramTypeDecl); - } - } - sb.append(')'); - - return sb.toString(); - } - - public static Object[] invocationErrorMessageStart(Member member) { - return invocationErrorMessageStart(member, member instanceof Constructor); - } - - private static Object[] invocationErrorMessageStart(Object member, boolean isConstructor) { - return new Object[] { "Java ", isConstructor ? "constructor " : "method ", new _DelayedJQuote(member) }; - } - - public static TemplateModelException newInvocationTemplateModelException(Object object, Member member, Throwable e) { - return newInvocationTemplateModelException( - object, - member, - (member.getModifiers() & Modifier.STATIC) != 0, - member instanceof Constructor, - e); - } - - public static TemplateModelException newInvocationTemplateModelException(Object object, CallableMemberDescriptor callableMemberDescriptor, Throwable e) { - return newInvocationTemplateModelException( - object, - new _DelayedConversionToString(callableMemberDescriptor) { - @Override - protected String doConversion(Object callableMemberDescriptor) { - return ((CallableMemberDescriptor) callableMemberDescriptor).getDeclaration(); - } - }, - callableMemberDescriptor.isStatic(), - callableMemberDescriptor.isConstructor(), - e); - } - - private static TemplateModelException newInvocationTemplateModelException( - Object parentObject, Object member, boolean isStatic, boolean isConstructor, Throwable e) { - while (e instanceof InvocationTargetException) { - Throwable cause = ((InvocationTargetException) e).getTargetException(); - if (cause != null) { - e = cause; - } else { - break; - } - } - - return new _TemplateModelException(e, - invocationErrorMessageStart(member, isConstructor), - " threw an exception", - isStatic || isConstructor ? "" : new Object[] { - " when invoked on ", parentObject.getClass(), " object ", new _DelayedJQuote(parentObject) - }, - "; see cause exception in the Java stack trace."); - } - -} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/package.html ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/package.html b/src/main/java/org/apache/freemarker/core/model/impl/beans/package.html deleted file mode 100644 index bd6b36c..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/beans/package.html +++ /dev/null @@ -1,39 +0,0 @@ -<!-- - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. ---> -<html> -<head> -<title></title> -</head> -<body> - -<p>The {@linkplain org.apache.freemarker.core.model.impl.DefaultObjectWrapper default object wrapper} of FreeMarker uses -this to expose Java Beans and POJO-s to templates.</p> - -<p>Most of the issues dealing with beans are handled by the -{@link freemarker.ext.beans.BeansWrapper#wrap(Object)}and {@link -freemarker.ext.beans.BeansWrapper#getStaticModels()} methods. In normal cases, - these are the only methods -you should use to turn an arbitrary Java object into a -FreeMarker {@link freemarker.template.TemplateModel}. Additionally, you can manually create -instance of any wrapper class using its constructors. -Note, however that in such cases you bypass the eventual model caching -of the wrapper.</p> - -</body> -</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/package.html ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/package.html b/src/main/java/org/apache/freemarker/core/model/impl/package.html new file mode 100644 index 0000000..100c5c5 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/model/impl/package.html @@ -0,0 +1,39 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +--> +<html> +<head> +<title></title> +</head> +<body> + +<p>The {@linkplain org.apache.freemarker.core.model.impl.DefaultObjectWrapper default object wrapper} of FreeMarker uses +this to expose Java Beans and POJO-s to templates.</p> + +<p>Most of the issues dealing with beans are handled by the +{@link freemarker.ext.beans.DefaultObjectWrapper#wrap(Object)}and {@link +freemarker.ext.beans.DefaultObjectWrapper#getStaticModels()} methods. In normal cases, + these are the only methods +you should use to turn an arbitrary Java object into a +FreeMarker {@link freemarker.template.TemplateModel}. Additionally, you can manually create +instance of any wrapper class using its constructors. +Note, however that in such cases you bypass the eventual model caching +of the wrapper.</p> + +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/util/FTLUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/util/FTLUtil.java b/src/main/java/org/apache/freemarker/core/util/FTLUtil.java index 9f0cca7..eba25f6 100644 --- a/src/main/java/org/apache/freemarker/core/util/FTLUtil.java +++ b/src/main/java/org/apache/freemarker/core/util/FTLUtil.java @@ -43,17 +43,17 @@ import org.apache.freemarker.core.model.TemplateScalarModel; import org.apache.freemarker.core.model.TemplateSequenceModel; import org.apache.freemarker.core.model.TemplateTransformModel; import org.apache.freemarker.core.model.WrapperTemplateModel; -import org.apache.freemarker.core.model.impl.beans.BeanModel; -import org.apache.freemarker.core.model.impl.beans.BooleanModel; -import org.apache.freemarker.core.model.impl.beans.CollectionModel; -import org.apache.freemarker.core.model.impl.beans.DateModel; -import org.apache.freemarker.core.model.impl.beans.EnumerationModel; -import org.apache.freemarker.core.model.impl.beans.IteratorModel; -import org.apache.freemarker.core.model.impl.beans.MapModel; -import org.apache.freemarker.core.model.impl.beans.NumberModel; -import org.apache.freemarker.core.model.impl.beans.OverloadedMethodsModel; -import org.apache.freemarker.core.model.impl.beans.SimpleMethodModel; -import org.apache.freemarker.core.model.impl.beans.StringModel; +import org.apache.freemarker.core.model.impl.BeanModel; +import org.apache.freemarker.core.model.impl.BooleanModel; +import org.apache.freemarker.core.model.impl.CollectionModel; +import org.apache.freemarker.core.model.impl.DateModel; +import org.apache.freemarker.core.model.impl.EnumerationModel; +import org.apache.freemarker.core.model.impl.IteratorModel; +import org.apache.freemarker.core.model.impl.MapModel; +import org.apache.freemarker.core.model.impl.NumberModel; +import org.apache.freemarker.core.model.impl.OverloadedMethodsModel; +import org.apache.freemarker.core.model.impl.SimpleMethodModel; +import org.apache.freemarker.core.model.impl.StringModel; /** * Static utility methods that perform tasks specific to the FreeMarker Template Language (FTL). http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/util/_ClassUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/util/_ClassUtil.java b/src/main/java/org/apache/freemarker/core/util/_ClassUtil.java index 0401ce0..f97d186 100644 --- a/src/main/java/org/apache/freemarker/core/util/_ClassUtil.java +++ b/src/main/java/org/apache/freemarker/core/util/_ClassUtil.java @@ -19,7 +19,7 @@ package org.apache.freemarker.core.util; -import org.apache.freemarker.core.model.impl.beans.NumberModel; +import org.apache.freemarker.core.model.impl.NumberModel; public class _ClassUtil { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java index 25d09e5..0016241 100644 --- a/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java +++ b/src/main/java/org/apache/freemarker/servlet/FreemarkerServlet.java @@ -411,7 +411,6 @@ public class FreemarkerServlet extends HttpServlet { private static final String DEPR_INITPARAM_ENCODING = "DefaultEncoding"; private static final String DEPR_INITPARAM_OBJECT_WRAPPER = "ObjectWrapper"; private static final String DEPR_INITPARAM_WRAPPER_SIMPLE = "simple"; - private static final String DEPR_INITPARAM_WRAPPER_BEANS = "beans"; private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER = "TemplateExceptionHandler"; private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_RETHROW = "rethrow"; private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_DEBUG = "debug"; @@ -1298,9 +1297,6 @@ public class FreemarkerServlet extends HttpServlet { + Configurable.OBJECT_WRAPPER_KEY + " and " + DEPR_INITPARAM_OBJECT_WRAPPER); } - if (DEPR_INITPARAM_WRAPPER_BEANS.equals(wrapper)) { - return _StaticObjectWrappers.BEANS_WRAPPER; - } if (DEPR_INITPARAM_WRAPPER_SIMPLE.equals(wrapper)) { return _StaticObjectWrappers.SIMPLE_OBJECT_WRAPPER; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java b/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java index 956064a..e9e9bf4 100644 --- a/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java +++ b/src/main/java/org/apache/freemarker/servlet/jsp/CustomTagAndELFunctionCombiner.java @@ -33,7 +33,7 @@ import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateSequenceModel; import org.apache.freemarker.core.model.TemplateTransformModel; -import org.apache.freemarker.core.model.impl.beans.SimpleMethodModel; +import org.apache.freemarker.core.model.impl.SimpleMethodModel; import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util._ClassUtil; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/servlet/jsp/JspContextModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/servlet/jsp/JspContextModel.java b/src/main/java/org/apache/freemarker/servlet/jsp/JspContextModel.java index dcd7fcd..d07796c 100644 --- a/src/main/java/org/apache/freemarker/servlet/jsp/JspContextModel.java +++ b/src/main/java/org/apache/freemarker/servlet/jsp/JspContextModel.java @@ -46,7 +46,7 @@ implements @Override public TemplateModel get(String key) throws TemplateModelException { Object bean = scope == ANY_SCOPE ? pageContext.findAttribute(key) : pageContext.getAttribute(key, scope); - return _StaticObjectWrappers.BEANS_WRAPPER.wrap(bean); + return _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER.wrap(bean); } @Override http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java b/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java index 4711cd2..526d3d5 100644 --- a/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java +++ b/src/main/java/org/apache/freemarker/servlet/jsp/JspTagModelBase.java @@ -40,7 +40,7 @@ import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.impl._StaticObjectWrappers; -import org.apache.freemarker.core.model.impl.beans.BeansWrapper; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; import org.apache.freemarker.core.util._StringUtil; import org.apache.freemarker.servlet.jsp.SimpleTagDirectiveModel.TemplateExceptionWrapperJspException; @@ -84,7 +84,7 @@ class JspTagModelBase { if (args != null && !args.isEmpty()) { ObjectWrapperAndUnwrapper unwrapper = wrapper instanceof ObjectWrapperAndUnwrapper ? (ObjectWrapperAndUnwrapper) wrapper - : _StaticObjectWrappers.BEANS_WRAPPER; // [2.4] Throw exception in this case + : _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER; // [2.4] Throw exception in this case final Object[] argArray = new Object[1]; for (Iterator iter = args.entrySet().iterator(); iter.hasNext(); ) { final Map.Entry entry = (Map.Entry) iter.next(); @@ -102,7 +102,7 @@ class JspTagModelBase { } } else { if (arg instanceof BigDecimal) { - argArray[0] = BeansWrapper.coerceBigDecimal( + argArray[0] = DefaultObjectWrapper.coerceBigDecimal( (BigDecimal) arg, setterMethod.getParameterTypes()[0]); } try { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java b/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java index 930223c..67b336e 100644 --- a/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java +++ b/src/main/java/org/apache/freemarker/servlet/jsp/TaglibFactory.java @@ -68,7 +68,6 @@ import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateTransformModel; import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; -import org.apache.freemarker.core.model.impl.beans.BeansWrapper; import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util._ClassUtil; import org.apache.freemarker.core.util._NullArgumentException; @@ -306,7 +305,7 @@ public class TaglibFactory implements TemplateHashModel { * Sets the {@link ObjectWrapper} used when building the JSP tag library {@link TemplateHashModel}-s from the TLD-s. * Usually, it should be the same {@link ObjectWrapper} that will be used inside the templates. {@code null} value * is only supported for backward compatibility. For custom EL functions to be exposed, it must be non-{@code null} - * and an {@code intanceof} {@link BeansWrapper} (like typically, a {@link DefaultObjectWrapper}). + * and an {@code intanceof} {@link DefaultObjectWrapper} (like typically, a {@link DefaultObjectWrapper}). * * @since 2.3.22 */ @@ -1624,7 +1623,7 @@ public class TaglibFactory implements TemplateHashModel { private static final String E_LISTENER = "listener"; private static final String E_LISTENER_CLASS = "listener-class"; - private final BeansWrapper beansWrapper; + private final DefaultObjectWrapper defaultObjectWrapper; private final Map<String, TemplateModel> tagsAndFunctions = new HashMap<>(); private final List listeners = new ArrayList(); @@ -1642,16 +1641,16 @@ public class TaglibFactory implements TemplateHashModel { private String listenerClassCData; TldParserForTaglibBuilding(ObjectWrapper wrapper) { - if (wrapper instanceof BeansWrapper) { - beansWrapper = (BeansWrapper) wrapper; + if (wrapper instanceof DefaultObjectWrapper) { + defaultObjectWrapper = (DefaultObjectWrapper) wrapper; } else { - beansWrapper = null; + defaultObjectWrapper = null; if (LOG.isWarnEnabled()) { LOG.warn("Custom EL functions won't be loaded because " + (wrapper == null ? "no ObjectWrapper was specified for the TaglibFactory " + "(via TaglibFactory.setObjectWrapper(...), exists since 2.3.22)" - : "the ObjectWrapper wasn't instance of " + BeansWrapper.class.getName()) + : "the ObjectWrapper wasn't instance of " + DefaultObjectWrapper.class.getName()) + "."); } } @@ -1747,7 +1746,7 @@ public class TaglibFactory implements TemplateHashModel { tagNameCData = null; tagClassCData = null; - } else if (E_FUNCTION.equals(qName) && beansWrapper != null) { + } else if (E_FUNCTION.equals(qName) && defaultObjectWrapper != null) { checkChildElementNotNull(qName, E_FUNCTION_CLASS, functionClassCData); checkChildElementNotNull(qName, E_FUNCTION_SIGNATURE, functionSignatureCData); checkChildElementNotNull(qName, E_NAME, functionNameCData); @@ -1777,7 +1776,7 @@ public class TaglibFactory implements TemplateHashModel { final TemplateMethodModelEx elFunctionModel; try { - elFunctionModel = beansWrapper.wrap(null, functionMethod); + elFunctionModel = defaultObjectWrapper.wrap(null, functionMethod); } catch (Exception e) { throw new TldParsingSAXException( "FreeMarker object wrapping failed on method : " + functionMethod, http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/resources/org/apache/freemarker/core/model/impl/beans/unsafeMethods.properties ---------------------------------------------------------------------- diff --git a/src/main/resources/org/apache/freemarker/core/model/impl/beans/unsafeMethods.properties b/src/main/resources/org/apache/freemarker/core/model/impl/beans/unsafeMethods.properties deleted file mode 100644 index f1f4e18..0000000 --- a/src/main/resources/org/apache/freemarker/core/model/impl/beans/unsafeMethods.properties +++ /dev/null @@ -1,98 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. - -java.lang.Object.wait() -java.lang.Object.wait(long) -java.lang.Object.wait(long,int) -java.lang.Object.notify() -java.lang.Object.notifyAll() - -java.lang.Class.getClassLoader() -java.lang.Class.newInstance() -java.lang.Class.forName(java.lang.String) -java.lang.Class.forName(java.lang.String,boolean,java.lang.ClassLoader) - -java.lang.reflect.Constructor.newInstance([Ljava.lang.Object;) - -java.lang.reflect.Method.invoke(java.lang.Object,[Ljava.lang.Object;) - -java.lang.reflect.Field.set(java.lang.Object,java.lang.Object) -java.lang.reflect.Field.setBoolean(java.lang.Object,boolean) -java.lang.reflect.Field.setByte(java.lang.Object,byte) -java.lang.reflect.Field.setChar(java.lang.Object,char) -java.lang.reflect.Field.setDouble(java.lang.Object,double) -java.lang.reflect.Field.setFloat(java.lang.Object,float) -java.lang.reflect.Field.setInt(java.lang.Object,int) -java.lang.reflect.Field.setLong(java.lang.Object,long) -java.lang.reflect.Field.setShort(java.lang.Object,short) - -java.lang.reflect.AccessibleObject.setAccessible([Ljava.lang.reflect.AccessibleObject;,boolean) -java.lang.reflect.AccessibleObject.setAccessible(boolean) - -java.lang.Thread.destroy() -java.lang.Thread.getContextClassLoader() -java.lang.Thread.interrupt() -java.lang.Thread.join() -java.lang.Thread.join(long) -java.lang.Thread.join(long,int) -java.lang.Thread.resume() -java.lang.Thread.run() -java.lang.Thread.setContextClassLoader(java.lang.ClassLoader) -java.lang.Thread.setDaemon(boolean) -java.lang.Thread.setName(java.lang.String) -java.lang.Thread.setPriority(int) -java.lang.Thread.sleep(long) -java.lang.Thread.sleep(long,int) -java.lang.Thread.start() -java.lang.Thread.stop() -java.lang.Thread.stop(java.lang.Throwable) -java.lang.Thread.suspend() - -java.lang.ThreadGroup.allowThreadSuspension(boolean) -java.lang.ThreadGroup.destroy() -java.lang.ThreadGroup.interrupt() -java.lang.ThreadGroup.resume() -java.lang.ThreadGroup.setDaemon(boolean) -java.lang.ThreadGroup.setMaxPriority(int) -java.lang.ThreadGroup.stop() -java.lang.Thread.suspend() - -java.lang.Runtime.addShutdownHook(java.lang.Thread) -java.lang.Runtime.exec(java.lang.String) -java.lang.Runtime.exec([Ljava.lang.String;) -java.lang.Runtime.exec([Ljava.lang.String;,[Ljava.lang.String;) -java.lang.Runtime.exec([Ljava.lang.String;,[Ljava.lang.String;,java.io.File) -java.lang.Runtime.exec(java.lang.String,[Ljava.lang.String;) -java.lang.Runtime.exec(java.lang.String,[Ljava.lang.String;,java.io.File) -java.lang.Runtime.exit(int) -java.lang.Runtime.halt(int) -java.lang.Runtime.load(java.lang.String) -java.lang.Runtime.loadLibrary(java.lang.String) -java.lang.Runtime.removeShutdownHook(java.lang.Thread) -java.lang.Runtime.traceInstructions(boolean) -java.lang.Runtime.traceMethodCalls(boolean) - -java.lang.System.exit(int) -java.lang.System.load(java.lang.String) -java.lang.System.loadLibrary(java.lang.String) -java.lang.System.runFinalizersOnExit(boolean) -java.lang.System.setErr(java.io.PrintStream) -java.lang.System.setIn(java.io.InputStream) -java.lang.System.setOut(java.io.PrintStream) -java.lang.System.setProperties(java.util.Properties) -java.lang.System.setProperty(java.lang.String,java.lang.String) -java.lang.System.setSecurityManager(java.lang.SecurityManager) http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/resources/org/apache/freemarker/core/model/impl/unsafeMethods.properties ---------------------------------------------------------------------- diff --git a/src/main/resources/org/apache/freemarker/core/model/impl/unsafeMethods.properties b/src/main/resources/org/apache/freemarker/core/model/impl/unsafeMethods.properties new file mode 100644 index 0000000..f1f4e18 --- /dev/null +++ b/src/main/resources/org/apache/freemarker/core/model/impl/unsafeMethods.properties @@ -0,0 +1,98 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +java.lang.Object.wait() +java.lang.Object.wait(long) +java.lang.Object.wait(long,int) +java.lang.Object.notify() +java.lang.Object.notifyAll() + +java.lang.Class.getClassLoader() +java.lang.Class.newInstance() +java.lang.Class.forName(java.lang.String) +java.lang.Class.forName(java.lang.String,boolean,java.lang.ClassLoader) + +java.lang.reflect.Constructor.newInstance([Ljava.lang.Object;) + +java.lang.reflect.Method.invoke(java.lang.Object,[Ljava.lang.Object;) + +java.lang.reflect.Field.set(java.lang.Object,java.lang.Object) +java.lang.reflect.Field.setBoolean(java.lang.Object,boolean) +java.lang.reflect.Field.setByte(java.lang.Object,byte) +java.lang.reflect.Field.setChar(java.lang.Object,char) +java.lang.reflect.Field.setDouble(java.lang.Object,double) +java.lang.reflect.Field.setFloat(java.lang.Object,float) +java.lang.reflect.Field.setInt(java.lang.Object,int) +java.lang.reflect.Field.setLong(java.lang.Object,long) +java.lang.reflect.Field.setShort(java.lang.Object,short) + +java.lang.reflect.AccessibleObject.setAccessible([Ljava.lang.reflect.AccessibleObject;,boolean) +java.lang.reflect.AccessibleObject.setAccessible(boolean) + +java.lang.Thread.destroy() +java.lang.Thread.getContextClassLoader() +java.lang.Thread.interrupt() +java.lang.Thread.join() +java.lang.Thread.join(long) +java.lang.Thread.join(long,int) +java.lang.Thread.resume() +java.lang.Thread.run() +java.lang.Thread.setContextClassLoader(java.lang.ClassLoader) +java.lang.Thread.setDaemon(boolean) +java.lang.Thread.setName(java.lang.String) +java.lang.Thread.setPriority(int) +java.lang.Thread.sleep(long) +java.lang.Thread.sleep(long,int) +java.lang.Thread.start() +java.lang.Thread.stop() +java.lang.Thread.stop(java.lang.Throwable) +java.lang.Thread.suspend() + +java.lang.ThreadGroup.allowThreadSuspension(boolean) +java.lang.ThreadGroup.destroy() +java.lang.ThreadGroup.interrupt() +java.lang.ThreadGroup.resume() +java.lang.ThreadGroup.setDaemon(boolean) +java.lang.ThreadGroup.setMaxPriority(int) +java.lang.ThreadGroup.stop() +java.lang.Thread.suspend() + +java.lang.Runtime.addShutdownHook(java.lang.Thread) +java.lang.Runtime.exec(java.lang.String) +java.lang.Runtime.exec([Ljava.lang.String;) +java.lang.Runtime.exec([Ljava.lang.String;,[Ljava.lang.String;) +java.lang.Runtime.exec([Ljava.lang.String;,[Ljava.lang.String;,java.io.File) +java.lang.Runtime.exec(java.lang.String,[Ljava.lang.String;) +java.lang.Runtime.exec(java.lang.String,[Ljava.lang.String;,java.io.File) +java.lang.Runtime.exit(int) +java.lang.Runtime.halt(int) +java.lang.Runtime.load(java.lang.String) +java.lang.Runtime.loadLibrary(java.lang.String) +java.lang.Runtime.removeShutdownHook(java.lang.Thread) +java.lang.Runtime.traceInstructions(boolean) +java.lang.Runtime.traceMethodCalls(boolean) + +java.lang.System.exit(int) +java.lang.System.load(java.lang.String) +java.lang.System.loadLibrary(java.lang.String) +java.lang.System.runFinalizersOnExit(boolean) +java.lang.System.setErr(java.io.PrintStream) +java.lang.System.setIn(java.io.InputStream) +java.lang.System.setOut(java.io.PrintStream) +java.lang.System.setProperties(java.util.Properties) +java.lang.System.setProperty(java.lang.String,java.lang.String) +java.lang.System.setSecurityManager(java.lang.SecurityManager) http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/manual/en_US/FM3-CHANGE-LOG.txt ---------------------------------------------------------------------- diff --git a/src/manual/en_US/FM3-CHANGE-LOG.txt b/src/manual/en_US/FM3-CHANGE-LOG.txt index 9486c33..72498a6 100644 --- a/src/manual/en_US/FM3-CHANGE-LOG.txt +++ b/src/manual/en_US/FM3-CHANGE-LOG.txt @@ -73,9 +73,7 @@ the FreeMarer 3 changelog here: OutputFormat realted classes were moved to org.apache.freemarker.core.outputformat. ValueFormat related classes were moved to org.apache.freemarker.core.valueformat. ArithmeticEngine related classes were moved to org.apache.freemarker.core.arithmetic. - freemarker.ext.beans were moved under org.apache.freemarker.core.model.impl.beans for now (but later - we only want a DefaultObject wrapper, no BeansWrapper, so this will change) and freemarker.ext.dom - was moved to org.apache.freemarker.dom. + freemarker.ext.dom was moved into org.apache.freemarker.dom. - Moved the all the static final ObjectWrapper-s to the new _StaticObjectWrappers class, and made them write protected (non-configurable). Also now they come from the pool that ObjectWrapper builders use. - WrappingTemplateModel.objectWrapper is now final, and its statically stored default value can't be set anymore. @@ -119,4 +117,4 @@ the FreeMarer 3 changelog here: Also, on the places where ParseException was used for other than template parsing, o.a.f.core.util.GenericParseException is used now instead, which doesn't have the template parsing related fields that we can't fill. - Removed DefaultObjectWrapper settings that only exist so that you can set backward compatible behavior instead of the - recommended value: useAdaptersForContainers, forceLegacyNonListCollections, iterableSupport \ No newline at end of file + recommended value: useAdaptersForContainers, forceLegacyNonListCollections, iterableSupport, simpleMapWrapper \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/ConfigurationTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java index 8413770..ee4bcbd 100644 --- a/src/test/java/org/apache/freemarker/core/ConfigurationTest.java +++ b/src/test/java/org/apache/freemarker/core/ConfigurationTest.java @@ -42,10 +42,9 @@ import org.apache.freemarker.core.Configurable.UnknownSettingException; import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateScalarModel; import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder; import org.apache.freemarker.core.model.impl.SimpleScalar; import org.apache.freemarker.core.model.impl._StaticObjectWrappers; -import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder; -import org.apache.freemarker.core.model.impl.beans.StringModel; import org.apache.freemarker.core.outputformat.MarkupOutputFormat; import org.apache.freemarker.core.outputformat.OutputFormat; import org.apache.freemarker.core.outputformat.UnregisteredOutputFormatException; @@ -1081,12 +1080,12 @@ public class ConfigurationTest extends TestCase { cfg.setSharedVariable("b", "bbLegacy"); // Cause re-wrapping of variables added via setSharedVaribles: - cfg.setObjectWrapper(new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build()); + cfg.setObjectWrapper(new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build()); { TemplateScalarModel aVal = (TemplateScalarModel) cfg.getSharedVariable("a"); assertEquals("aa", aVal.getAsString()); - assertEquals(StringModel.class, aVal.getClass()); + assertEquals(SimpleScalar.class, aVal.getClass()); TemplateScalarModel bVal = (TemplateScalarModel) cfg.getSharedVariable("b"); assertEquals("bbLegacy", bVal.getAsString()); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/IteratorIssuesTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/IteratorIssuesTest.java b/src/test/java/org/apache/freemarker/core/IteratorIssuesTest.java index 224ff7d..aa9e66b 100644 --- a/src/test/java/org/apache/freemarker/core/IteratorIssuesTest.java +++ b/src/test/java/org/apache/freemarker/core/IteratorIssuesTest.java @@ -23,52 +23,37 @@ import java.util.Iterator; import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder; -import org.apache.freemarker.core.model.impl.beans.BeansWrapper; -import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder; import org.apache.freemarker.test.TemplateTest; import org.junit.Test; public class IteratorIssuesTest extends TemplateTest { - + + private static final DefaultObjectWrapper OW = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build(); + private static final String FTL_HAS_CONTENT_AND_LIST = "<#if it?hasContent><#list it as i>${i}</#list><#else>empty</#if>"; private static final String OUT_HAS_CONTENT_AND_LIST_ABC = "abc"; private static final String OUT_HAS_CONTENT_AND_LIST_EMPTY = "empty"; - + private static final String FTL_LIST_AND_HAS_CONTENT = "<#list it as i>${i}${it?hasContent?then('+', '-')}</#list>"; private static final String OUT_LIST_AND_HAS_CONTENT_BW_GOOD = "a+b+c-"; @Test - public void testHasContentAndListDOW() throws Exception { - addToDataModel("it", getDOW300().wrap(getAbcIt())); + public void testHasContentAndList() throws Exception { + addToDataModel("it", OW.wrap(getAbcIt())); assertOutput(FTL_HAS_CONTENT_AND_LIST, OUT_HAS_CONTENT_AND_LIST_ABC); - - addToDataModel("it", getDOW300().wrap(getEmptyIt())); - assertOutput(FTL_HAS_CONTENT_AND_LIST, OUT_HAS_CONTENT_AND_LIST_EMPTY); - } - @Test - public void testHasContentAndListBW() throws Exception { - addToDataModel("it", getBW300().wrap(getAbcIt())); - assertOutput(FTL_HAS_CONTENT_AND_LIST, OUT_HAS_CONTENT_AND_LIST_ABC); - - addToDataModel("it", getBW300().wrap(getEmptyIt())); + addToDataModel("it", OW.wrap(getEmptyIt())); assertOutput(FTL_HAS_CONTENT_AND_LIST, OUT_HAS_CONTENT_AND_LIST_EMPTY); } - + @Test - public void testListAndHasContentDOW() throws Exception { - addToDataModel("it", getDOW300().wrap(getAbcIt())); + public void testListAndHasContent() throws Exception { + addToDataModel("it", OW.wrap(getAbcIt())); assertErrorContains(FTL_LIST_AND_HAS_CONTENT, "can be listed only once"); } - @Test - public void testListAndHasContentBW() throws Exception { - addToDataModel("it", getBW300().wrap(getAbcIt())); - assertOutput(FTL_LIST_AND_HAS_CONTENT, OUT_LIST_AND_HAS_CONTENT_BW_GOOD); - } - private Iterator getAbcIt() { return Arrays.asList(new String[] { "a", "b", "c" }).iterator(); } @@ -76,13 +61,5 @@ public class IteratorIssuesTest extends TemplateTest { private Iterator getEmptyIt() { return Arrays.asList(new String[] { }).iterator(); } - - private DefaultObjectWrapper getDOW300() { - return new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build(); - } - private BeansWrapper getBW300() { - return new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build(); - } - } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/ObjectBuilderSettingsTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/ObjectBuilderSettingsTest.java b/src/test/java/org/apache/freemarker/core/ObjectBuilderSettingsTest.java index 5a702d4..ce91c66 100644 --- a/src/test/java/org/apache/freemarker/core/ObjectBuilderSettingsTest.java +++ b/src/test/java/org/apache/freemarker/core/ObjectBuilderSettingsTest.java @@ -42,7 +42,6 @@ import org.apache.freemarker.core.arithmetic.ArithmeticEngine; import org.apache.freemarker.core.arithmetic.impl.BigDecimalArithmeticEngine; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; -import org.apache.freemarker.core.model.impl.beans.BeansWrapper; import org.apache.freemarker.core.templateresolver.CacheStorage; import org.apache.freemarker.core.templateresolver.TemplateLoader; import org.apache.freemarker.core.templateresolver.TemplateLoaderSession; @@ -359,24 +358,23 @@ public class ObjectBuilderSettingsTest { assertNull(res.b4.b4); } } - + @Test - public void beansWrapperTest() throws Exception { - BeansWrapper bw = (BeansWrapper) _ObjectBuilderSettingEvaluator.eval( - "BeansWrapper(3.0.0, simpleMapWrapper=true, exposeFields=true)", + public void defaultObjectWrapperTest() throws Exception { + DefaultObjectWrapper ow = (DefaultObjectWrapper) _ObjectBuilderSettingEvaluator.eval( + "DefaultObjectWrapper(3.0.0)", ObjectWrapper.class, false, _SettingEvaluationEnvironment.getCurrent()); - assertEquals(Configuration.VERSION_3_0_0, bw.getIncompatibleImprovements()); - assertTrue(bw.isSimpleMapWrapper()); - assertTrue(bw.isExposeFields()); + assertEquals(Configuration.VERSION_3_0_0, ow.getIncompatibleImprovements()); + assertFalse(ow.isExposeFields()); } @Test - public void defaultObjectWrapperTest() throws Exception { - DefaultObjectWrapper bw = (DefaultObjectWrapper) _ObjectBuilderSettingEvaluator.eval( - "DefaultObjectWrapper(3.0.0)", + public void defaultObjectWrapperTest2() throws Exception { + DefaultObjectWrapper ow = (DefaultObjectWrapper) _ObjectBuilderSettingEvaluator.eval( + "DefaultObjectWrapper(3.0.0, exposeFields=true)", ObjectWrapper.class, false, _SettingEvaluationEnvironment.getCurrent()); - assertEquals(Configuration.VERSION_3_0_0, bw.getIncompatibleImprovements()); - assertFalse(bw.isExposeFields()); + assertEquals(Configuration.VERSION_3_0_0, ow.getIncompatibleImprovements()); + assertTrue(ow.isExposeFields()); } @Test @@ -386,7 +384,7 @@ public class ObjectBuilderSettingsTest { { Properties props = new Properties(); props.setProperty(Configurable.OBJECT_WRAPPER_KEY, - "org.apache.freemarker.core.model.impl.beans.BeansWrapper(3.0.0)"); + "org.apache.freemarker.core.model.impl.DefaultObjectWrapper(3.0.0)"); props.setProperty(Configurable.ARITHMETIC_ENGINE_KEY, "org.apache.freemarker.core.ObjectBuilderSettingsTest$DummyArithmeticEngine"); props.setProperty(Configurable.TEMPLATE_EXCEPTION_HANDLER_KEY, @@ -399,10 +397,10 @@ public class ObjectBuilderSettingsTest { props.setProperty(Configuration.TEMPLATE_LOADER_KEY, "org.apache.freemarker.core.ObjectBuilderSettingsTest$DummyTemplateLoader()"); cfg.setSettings(props); - assertEquals(BeansWrapper.class, cfg.getObjectWrapper().getClass()); + assertEquals(DefaultObjectWrapper.class, cfg.getObjectWrapper().getClass()); assertTrue(((WriteProtectable) cfg.getObjectWrapper()).isWriteProtected()); assertEquals( - Configuration.VERSION_3_0_0, ((BeansWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements()); + Configuration.VERSION_3_0_0, ((DefaultObjectWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements()); assertEquals(DummyArithmeticEngine.class, cfg.getArithmeticEngine().getClass()); assertEquals(DummyTemplateExceptionHandler.class, cfg.getTemplateExceptionHandler().getClass()); assertEquals(DummyCacheStorage.class, cfg.getCacheStorage().getClass()); @@ -428,7 +426,7 @@ public class ObjectBuilderSettingsTest { assertEquals(1, ((DummyArithmeticEngine) cfg.getArithmeticEngine()).getX()); assertEquals(1, ((DummyTemplateExceptionHandler) cfg.getTemplateExceptionHandler()).getX()); assertEquals(Configuration.VERSION_3_0_0, - ((BeansWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements()); + ((DefaultObjectWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements()); assertEquals(500, ((MruCacheStorage) cfg.getCacheStorage()).getSoftSizeLimit()); assertEquals(TemplateClassResolver.ALLOWS_NOTHING_RESOLVER, cfg.getNewBuiltinClassResolver()); assertEquals("utf-8", cfg.getDefaultEncoding()); @@ -436,16 +434,16 @@ public class ObjectBuilderSettingsTest { { Properties props = new Properties(); - props.setProperty(Configurable.OBJECT_WRAPPER_KEY, "Beans"); + props.setProperty(Configurable.OBJECT_WRAPPER_KEY, "Default"); props.setProperty(Configurable.ARITHMETIC_ENGINE_KEY, "bigdecimal"); props.setProperty(Configurable.TEMPLATE_EXCEPTION_HANDLER_KEY, "rethrow"); cfg.setSettings(props); - assertEquals(BeansWrapper.class, cfg.getObjectWrapper().getClass()); + assertEquals(DefaultObjectWrapper.class, cfg.getObjectWrapper().getClass()); assertSame(BigDecimalArithmeticEngine.INSTANCE, cfg.getArithmeticEngine()); assertSame(TemplateExceptionHandler.RETHROW_HANDLER, cfg.getTemplateExceptionHandler()); assertTrue(((WriteProtectable) cfg.getObjectWrapper()).isWriteProtected()); assertEquals(Configuration.VERSION_3_0_0, - ((BeansWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements()); + ((DefaultObjectWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements()); } { @@ -456,7 +454,7 @@ public class ObjectBuilderSettingsTest { assertTrue(((WriteProtectable) cfg.getObjectWrapper()).isWriteProtected()); assertEquals( Configuration.VERSION_3_0_0, - ((BeansWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements()); + ((DefaultObjectWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements()); } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java b/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java new file mode 100644 index 0000000..b555e59 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/model/impl/AbstractParallelIntrospectionTest.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.freemarker.core.model.impl; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.model.TemplateHashModel; +import org.apache.freemarker.core.model.TemplateMethodModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateNumberModel; + +import junit.framework.TestCase; + +public abstract class AbstractParallelIntrospectionTest extends TestCase { + + private static final int NUM_THREADS = 8; + private static final int NUM_ENTITYES = 8; + private static final int NUM_MEMBERS = 8; + private static final int ITERATIONS = 20000; + private static final double CACHE_CLEARING_CHANCE = 0.01; + + private DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0); + + public AbstractParallelIntrospectionTest(String name) { + super(name); + } + + public void testReliability() { + testReliability(ITERATIONS); + } + + public void testReliability(int iterations) { + TestThread[] ts = new TestThread[NUM_THREADS]; + for (int i = 0; i < NUM_THREADS; i++) { + ts[i] = new TestThread(iterations); + ts[i].start(); + } + + for (int i = 0; i < NUM_THREADS; i++) { + try { + ts[i].join(); + if (ts[i].error != null) { + throw new AssertionError(ts[i].error); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + protected abstract TemplateHashModel getWrappedEntity(int objIdx) throws TemplateModelException; + + protected final DefaultObjectWrapper getObjectWrapper() { + return ow; + } + + private class TestThread extends Thread { + + private final int iterations; + + private Throwable error; + + private TestThread(int iterations) { + this.iterations = iterations; + } + + @Override + public void run() { + try { + for (int i = 0; i < iterations; i++) { + if (Math.random() < CACHE_CLEARING_CHANCE) { + ow.clearClassIntrospecitonCache(); + } + int objIdx = (int) (Math.random() * NUM_ENTITYES); + TemplateHashModel h = getWrappedEntity(objIdx); + int mIdx = (int) (Math.random() * NUM_MEMBERS); + testProperty(h, objIdx, mIdx); + testMethod(h, objIdx, mIdx); + } + } catch (Throwable e) { + error = e; + } + } + + private void testProperty(TemplateHashModel h, int objIdx, int mIdx) + throws TemplateModelException, AssertionError { + TemplateNumberModel pv = (TemplateNumberModel) h.get("p" + mIdx); + final int expected = objIdx * 1000 + mIdx; + final int got = pv.getAsNumber().intValue(); + if (got != expected) { + throw new AssertionError("Property assertation failed; " + + "expected " + expected + ", but got " + got); + } + } + + private void testMethod(TemplateHashModel h, int objIdx, int mIdx) + throws TemplateModelException, AssertionError { + TemplateMethodModel pv = (TemplateMethodModel) h.get("m" + mIdx); + final int expected = objIdx * 1000 + mIdx; + final int got = ((TemplateNumberModel) pv.exec(null)).getAsNumber().intValue(); + if (got != expected) { + throw new AssertionError("Method assertation failed; " + + "expected " + expected + ", but got " + got); + } + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/AlphabeticalMethodSorter.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/model/impl/AlphabeticalMethodSorter.java b/src/test/java/org/apache/freemarker/core/model/impl/AlphabeticalMethodSorter.java new file mode 100644 index 0000000..572bd39 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/model/impl/AlphabeticalMethodSorter.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.freemarker.core.model.impl; + +import java.beans.MethodDescriptor; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; + +class AlphabeticalMethodSorter implements MethodSorter { + + private final boolean desc; + + public AlphabeticalMethodSorter(boolean desc) { + this.desc = desc; + } + + @Override + public MethodDescriptor[] sortMethodDescriptors(MethodDescriptor[] methodDescriptors) { + ArrayList<MethodDescriptor> ls = new ArrayList<>(Arrays.asList(methodDescriptors)); + Collections.sort(ls, new Comparator<MethodDescriptor>() { + @Override + public int compare(MethodDescriptor o1, MethodDescriptor o2) { + int res = o1.getMethod().toString().compareTo(o2.getMethod().toString()); + return desc ? -res : res; + } + }); + return ls.toArray(new MethodDescriptor[ls.size()]); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/CommonSupertypeForUnwrappingHintTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/model/impl/CommonSupertypeForUnwrappingHintTest.java b/src/test/java/org/apache/freemarker/core/model/impl/CommonSupertypeForUnwrappingHintTest.java new file mode 100644 index 0000000..89c488a --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/model/impl/CommonSupertypeForUnwrappingHintTest.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.freemarker.core.model.impl; + +import java.io.Serializable; +import java.util.List; + +import org.apache.freemarker.core.model.TemplateModelException; + +import junit.framework.TestCase; + +public class CommonSupertypeForUnwrappingHintTest extends TestCase { + + final OverloadedMethodsSubset oms = new DummyOverloadedMethodsSubset(); + + public CommonSupertypeForUnwrappingHintTest(String name) { + super(name); + } + + public void testInterfaces() { + assertEquals(Serializable.class, oms.getCommonSupertypeForUnwrappingHint(String.class, Number.class)); + assertEquals(C1I1.class, oms.getCommonSupertypeForUnwrappingHint(C2ExtC1I1.class, C3ExtC1I1.class)); + assertEquals(Object.class, oms.getCommonSupertypeForUnwrappingHint(C3I1I2.class, C4I1I2.class)); + assertEquals(I1.class, oms.getCommonSupertypeForUnwrappingHint(C3I1I2.class, C5I1.class)); + assertEquals(I1.class, oms.getCommonSupertypeForUnwrappingHint(C3I1I2.class, I1.class)); + assertEquals(I2.class, oms.getCommonSupertypeForUnwrappingHint(C3I1I2.class, I2.class)); + assertEquals(I1.class, oms.getCommonSupertypeForUnwrappingHint(I1I2.class, I1.class)); + assertEquals(I2.class, oms.getCommonSupertypeForUnwrappingHint(I1I2.class, I2.class)); + assertEquals(CharSequence.class, oms.getCommonSupertypeForUnwrappingHint(String.class, StringBuilder.class)); + assertEquals(C6.class, oms.getCommonSupertypeForUnwrappingHint(C7ExtC6I1.class, C8ExtC6I1.class)); + } + + public void testArrayAndOther() { + testArrayAndOther(oms); + } + + /** These will be the same with oms and buggy: */ + private void testArrayAndOther(OverloadedMethodsSubset oms) { + assertEquals(Serializable.class, oms.getCommonSupertypeForUnwrappingHint(int[].class, String.class)); + assertEquals(Serializable.class, oms.getCommonSupertypeForUnwrappingHint(Object[].class, String.class)); + + assertEquals(Object.class, oms.getCommonSupertypeForUnwrappingHint(int[].class, List.class)); + assertEquals(Object.class, oms.getCommonSupertypeForUnwrappingHint(Object[].class, List.class)); + + assertEquals(int[].class, oms.getCommonSupertypeForUnwrappingHint(int[].class, int[].class)); + assertEquals(Object[].class, oms.getCommonSupertypeForUnwrappingHint(Object[].class, Object[].class)); + } + + public void testArrayAndDifferentArray() { + assertEquals(Serializable.class, oms.getCommonSupertypeForUnwrappingHint(int[].class, Object[].class)); + assertEquals(Serializable.class, oms.getCommonSupertypeForUnwrappingHint(int[].class, long[].class)); + } + + public void testPrimitive() { + assertEquals(Integer.class, oms.getCommonSupertypeForUnwrappingHint(int.class, Integer.class)); + assertEquals(Integer.class, oms.getCommonSupertypeForUnwrappingHint(Integer.class, int.class)); + assertEquals(Number.class, oms.getCommonSupertypeForUnwrappingHint(int.class, Long.class)); + assertEquals(Number.class, oms.getCommonSupertypeForUnwrappingHint(Long.class, int.class)); + assertEquals(Number.class, oms.getCommonSupertypeForUnwrappingHint(Integer.class, long.class)); + assertEquals(Number.class, oms.getCommonSupertypeForUnwrappingHint(long.class, Integer.class)); + assertEquals(Boolean.class, oms.getCommonSupertypeForUnwrappingHint(boolean.class, Boolean.class)); + assertEquals(Boolean.class, oms.getCommonSupertypeForUnwrappingHint(Boolean.class, boolean.class)); + assertEquals(Character.class, oms.getCommonSupertypeForUnwrappingHint(char.class, Character.class)); + assertEquals(Character.class, oms.getCommonSupertypeForUnwrappingHint(Character.class, char.class)); + assertEquals(Number.class, oms.getCommonSupertypeForUnwrappingHint(int.class, short.class)); + assertEquals(Number.class, oms.getCommonSupertypeForUnwrappingHint(short.class, int.class)); + } + + public void testMisc() { + assertEquals(Number.class, oms.getCommonSupertypeForUnwrappingHint(Long.class, Integer.class)); + assertEquals(char.class, oms.getCommonSupertypeForUnwrappingHint(char.class, char.class)); + assertEquals(Integer.class, oms.getCommonSupertypeForUnwrappingHint(Integer.class, Integer.class)); + assertEquals(String.class, oms.getCommonSupertypeForUnwrappingHint(String.class, String.class)); + } + + static interface I1 { }; + static class C1I1 implements I1 { }; + static class C2ExtC1I1 extends C1I1 { }; + static class C3ExtC1I1 extends C1I1 { }; + static interface I2 { }; + static class C3I1I2 implements I1, I2 { }; + static class C4I1I2 implements I1, I2 { }; + static class C5I1 implements I1 { }; + static interface I1I2 extends I1, I2 { }; + static class C6 { }; + static class C7ExtC6I1 extends C6 implements I1 { }; + static class C8ExtC6I1 extends C6 implements I1 { }; + + private static class DummyOverloadedMethodsSubset extends OverloadedMethodsSubset { + + DummyOverloadedMethodsSubset() { + super(); + } + + @Override + Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) { + return memberDesc.getParamTypes(); + } + + @Override + void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes) { + // Do nothing + } + + @Override + MaybeEmptyMemberAndArguments getMemberAndArguments(List tmArgs, DefaultObjectWrapper w) throws TemplateModelException { + throw new RuntimeException("Not implemented in this dummy."); + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperDesc.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperDesc.java b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperDesc.java new file mode 100644 index 0000000..6946374 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperDesc.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.freemarker.core.model.impl; + +import org.apache.freemarker.core.Configuration; + +public class DefaultObjectWrapperDesc extends DefaultObjectWrapperWithSortedMethods { + + public DefaultObjectWrapperDesc() { + super(Configuration.VERSION_3_0_0, true); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperInc.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperInc.java b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperInc.java new file mode 100644 index 0000000..ec7ce7c --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperInc.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.freemarker.core.model.impl; + +import org.apache.freemarker.core.Configuration; + +public class DefaultObjectWrapperInc extends DefaultObjectWrapperWithSortedMethods { + + public DefaultObjectWrapperInc() { + super(Configuration.VERSION_3_0_0, false); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperModelFactoryRegistrationTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperModelFactoryRegistrationTest.java b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperModelFactoryRegistrationTest.java new file mode 100644 index 0000000..62e3e5f --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperModelFactoryRegistrationTest.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.freemarker.core.model.impl; + +import static org.junit.Assert.*; + +import java.lang.ref.Reference; + +import org.apache.freemarker.core.Configuration; +import org.junit.Test; + +public class DefaultObjectWrapperModelFactoryRegistrationTest { + + @Test + public void introspectionSettingChanges() { + DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0); + ClassIntrospector ci1 = ow.getClassIntrospector(); + checkRegisteredModelFactories(ci1, ow.getStaticModels(), ow.getEnumModels(), ow.getModelCache()); + + ow.setExposeFields(true); + ClassIntrospector ci2 = ow.getClassIntrospector(); + assertNotSame(ci1, ci2); + checkRegisteredModelFactories(ci1); + checkRegisteredModelFactories(ci2, ow.getStaticModels(), ow.getEnumModels(), ow.getModelCache()); + + ow.setExposureLevel(DefaultObjectWrapper.EXPOSE_ALL); + ClassIntrospector ci3 = ow.getClassIntrospector(); + assertNotSame(ci2, ci3); + checkRegisteredModelFactories(ci2); + checkRegisteredModelFactories(ci3, ow.getStaticModels(), ow.getEnumModels(), ow.getModelCache()); + + MethodAppearanceFineTuner maf = new MethodAppearanceFineTuner() { + @Override + public void process(DecisionInput in, Decision out) { + // nop + } + }; + ow.setMethodAppearanceFineTuner(maf); + ClassIntrospector ci4 = ow.getClassIntrospector(); + assertNotSame(ci3, ci4); + checkRegisteredModelFactories(ci3); + checkRegisteredModelFactories(ci4, ow.getStaticModels(), ow.getEnumModels(), ow.getModelCache()); + + ow.setExposeFields(true); + assertSame(ci4, ow.getClassIntrospector()); + ow.setExposureLevel(DefaultObjectWrapper.EXPOSE_ALL); + assertSame(ci4, ow.getClassIntrospector()); + ow.setMethodAppearanceFineTuner(maf); + assertSame(ci4, ow.getClassIntrospector()); + checkRegisteredModelFactories(ci4, ow.getStaticModels(), ow.getEnumModels(), ow.getModelCache()); + } + + private void checkRegisteredModelFactories(ClassIntrospector ci, Object... expected) { + Object[] actualRefs = ci.getRegisteredModelFactoriesSnapshot(); + + scanActuals: for (Object actaulRef : actualRefs) { + Object actualItem = ((Reference) actaulRef).get(); + for (Object expectedItem : expected) { + if (actualItem == expectedItem) { + continue scanActuals; + } + } + fail("Actual item " + actualItem + " is not among the expected items"); + } + + scanExpecteds: for (Object expectedItem : expected) { + for (Object ref : actualRefs) { + Object actualItem = ((Reference) ref).get(); + if (actualItem == expectedItem) { + continue scanExpecteds; + } + } + fail("Expected item " + expectedItem + " is not among the actual items"); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperReadOnlyTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperReadOnlyTest.java b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperReadOnlyTest.java new file mode 100644 index 0000000..ab18f5d --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperReadOnlyTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.freemarker.core.model.impl; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.util.WriteProtectable; +import org.apache.freemarker.core.util._ClassUtil; + +import junit.framework.TestCase; + +/** + * Tests if all JavaBean properties of {@link DefaultObjectWrapper} are locked by + * {@link WriteProtectable#writeProtect()}. + */ +public class DefaultObjectWrapperReadOnlyTest extends TestCase { + + private static final String EXPECTED_MESSAGE_PART = "write protected"; + + public DefaultObjectWrapperReadOnlyTest(String name) { + super(name); + } + + public void testDefaultObjectWrapper() throws Exception { + DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0); + ow.writeProtect(); + checkAllPropertiesReadOnly(ow); + } + + private void checkAllPropertiesReadOnly(Object o) throws Exception { + BeanInfo bi = Introspector.getBeanInfo(o.getClass()); + for (PropertyDescriptor pd : bi.getPropertyDescriptors()) { + Method setter = pd.getWriteMethod(); + if (setter != null) { + Class t = pd.getPropertyType(); + + Object val; + if (_ClassUtil.isNumerical(t)) { + val = Byte.valueOf((byte) 1); + } else if (t == boolean.class) { + val = Boolean.TRUE; + } else if (t == char.class) { + val = Character.valueOf('c'); + } else if (t.isAssignableFrom(String.class)) { + val = "s"; + } else { + val = null; + } + try { + setter.invoke(o, val); + fail("This setter should have failed as the property should be read-only now: " + setter); + } catch (InvocationTargetException e) { + Throwable target = e.getTargetException(); + if (!(target instanceof IllegalStateException + && target.getMessage() != null + && target.getMessage().contains(EXPECTED_MESSAGE_PART))) { + fail("Expected IllegalStateException with message containing \"" + EXPECTED_MESSAGE_PART + + "\", got this instead: " + target); + } + } + } + } + } + +}
