You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/02/28 22:57:35 UTC
[01/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Repository: incubator-freemarker
Updated Branches:
refs/heads/3 3187a094e -> 051a08223
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/expected/beans.txt
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/expected/beans.txt b/src/test/resources/org/apache/freemarker/test/templatesuite/expected/beans.txt
deleted file mode 100644
index ab41e26..0000000
--- a/src/test/resources/org/apache/freemarker/test/templatesuite/expected/beans.txt
+++ /dev/null
@@ -1,56 +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.
- */
-array-0
-array-1
-2
-array-0
-array-1
-list-0
-list-1
-list-2
-3
-not empty
-list-0
-value
-objValue
-foo-value
-hasfoo
-nobaz
-bar-value-0
-bar-value-0
-foo-value
-overloaded-int-1
-overloaded-String-String
-Message
-1974-11-14
-static-method
-static-overloaded-int-1
-static-overloaded-String-String
-static-final-field
-static-field
-ONEx
-TWOx
-THREEx
-true
-false
-false
-42
-1
-m
-
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/expected/default-object-wrapper.txt
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/expected/default-object-wrapper.txt b/src/test/resources/org/apache/freemarker/test/templatesuite/expected/default-object-wrapper.txt
new file mode 100644
index 0000000..ab41e26
--- /dev/null
+++ b/src/test/resources/org/apache/freemarker/test/templatesuite/expected/default-object-wrapper.txt
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+array-0
+array-1
+2
+array-0
+array-1
+list-0
+list-1
+list-2
+3
+not empty
+list-0
+value
+objValue
+foo-value
+hasfoo
+nobaz
+bar-value-0
+bar-value-0
+foo-value
+overloaded-int-1
+overloaded-String-String
+Message
+1974-11-14
+static-method
+static-overloaded-int-1
+static-overloaded-String-String
+static-final-field
+static-field
+ONEx
+TWOx
+THREEx
+true
+false
+false
+42
+1
+m
+
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/expected/listhash.txt
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/expected/listhash.txt b/src/test/resources/org/apache/freemarker/test/templatesuite/expected/listhash.txt
index c713195..15b2efe 100644
--- a/src/test/resources/org/apache/freemarker/test/templatesuite/expected/listhash.txt
+++ b/src/test/resources/org/apache/freemarker/test/templatesuite/expected/listhash.txt
@@ -84,37 +84,6 @@ Non-empty maps:
Map:
[
- k1 = v1
- 2 = v2
- k3 = v3
- null = v4
- Y = v5
- N = null
- ]
-
- [
- k1 = v1; // @0=@0; odd=odd; Y=Y
- 2 = v2; // @1=@1; even=even; Y=Y
- k3 = v3; // @2=@2; odd=odd; Y=Y
- null = v4; // @3=@3; even=even; Y=Y
- Y = v5; // @4=@4; odd=odd; Y=Y
- N = null // @5=@5; even=even; N=N
- ]
-
- {
- [
- k1 = v1; // @0=@0; odd=odd; Y=Y
- 2 = v2; // @1=@1; even=even; Y=Y
- k3 = v3; // @2=@2; odd=odd; Y=Y
- null = v4; // @3=@3; even=even; Y=Y
- Y = v5; // @4=@4; odd=odd; Y=Y
- N = null // @5=@5; even=even; N=N
- ]
- }
-
- Map:
-
- [
k1 = 11
k2 = 22
]
@@ -173,19 +142,6 @@ Empty maps:
Empty
}
- Map:
-
- [
- ]
-
- [
- Empty
- ]
-
- {
- Empty
- }
-
a @ 0, 1
aa = 11 @ 0 // inside a @ 0, 1
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/templates/api-builtins.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/templates/api-builtins.ftl b/src/test/resources/org/apache/freemarker/test/templatesuite/templates/api-builtins.ftl
index ce5fc9f..c915104 100644
--- a/src/test/resources/org/apache/freemarker/test/templatesuite/templates/api-builtins.ftl
+++ b/src/test/resources/org/apache/freemarker/test/templatesuite/templates/api-builtins.ftl
@@ -30,17 +30,11 @@
</#list>
<@assertEquals expected="1: a, 2: b, 3: c" actual=dump />
-<#assign bw = testName?ends_with("-bw")>
-
<@assert test=map?has_api />
<@assert test=list?has_api />
<@assert test=set?has_api />
-<@assert test = bw == s?has_api />
+<@assert test=!s?has_api />
<@assert test=!1?has_api />
<@assert test=!""?has_api />
<@assert test=!{}?has_api />
<@assert test=!true?has_api />
-
-<#if bw>
- <@assertEquals expected="TEST" actual=s?api.toUpperCase() />
-</#if>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/templates/bean-maps.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/templates/bean-maps.ftl b/src/test/resources/org/apache/freemarker/test/templatesuite/templates/bean-maps.ftl
deleted file mode 100644
index e8651b8..0000000
--- a/src/test/resources/org/apache/freemarker/test/templatesuite/templates/bean-maps.ftl
+++ /dev/null
@@ -1,58 +0,0 @@
-[#ftl]
-[#--
- 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.
---]
-
-
-[#macro test hash desc]
-${desc} (${hash?size}):
- [#list hash?keys?sort as key]
- ${key}[#list 0 .. (16 - key?length) as x] [/#list]: [@print value=hash[key]/]
- [/#list]
-[/#macro]
-
-
-[#macro print value="DEBUGME"]
- [#if value?is_number || value?is_string]
- ${value}[#t]
- [#elseif value?is_boolean]
- [#if value]true[#else]false[/#if][#t]
- [#else]
- UNKNOWN[#t]
- [/#if]
-[/#macro]
-[@test hash=m1 desc="properties only, shadow"/]
-
-[@test hash=m2 desc="properties only"/]
-
-[@test hash=m3 desc="nothing, shadow"/]
-
-[@test hash=m4 desc="nothing"/]
-
-[@test hash=m5 desc="all, shadow"/]
-
-[@test hash=m6 desc="all"/]
-
-[@test hash=m7 desc="simple map mode"/]
-
-
-String concatenation:
- ${s1 + s2}
- ${s3 + s4}
- ${s1 + s3}
- ${s2 + s4}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/templates/beans.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/templates/beans.ftl b/src/test/resources/org/apache/freemarker/test/templatesuite/templates/beans.ftl
deleted file mode 100644
index 3b29c62..0000000
--- a/src/test/resources/org/apache/freemarker/test/templatesuite/templates/beans.ftl
+++ /dev/null
@@ -1,60 +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.
--->
-<#list array as item>
-${item}
-</#list>
-${array?size}
-${array[0]}
-${array[1]}
-<#list list as item>
-${item}
-</#list>
-${list.size()}
-<#if list.isEmpty()>empty<#else>not empty</#if>
-${list[0]}
-${map.key}
-${map(objKey)}
-${obj.foo}
-<#if obj.foo?exists>hasfoo<#else>nofoo</#if>
-<#if obj.baz?exists>hasbaz<#else>nobaz</#if>
-${obj.bar[0]}
-${obj.bar(0)}
-${obj.getFoo()}
-${obj.overloaded(1?int)}
-${obj.overloaded("String")}
-${resourceBundle.message}
-${resourceBundle("format", date)}
-<#assign static = statics["org.apache.freemarker.test.templatesuite.models.BeanTestClass"]>
-${static.staticMethod()}
-${static.staticOverloaded[1]}
-${static.staticOverloaded("String")}
-${static.STATIC_FINAL_FIELD}
-${static.STATIC_FIELD}
-<#assign enum = enums["org.apache.freemarker.test.templatesuite.models.EnumTestClass"]>
-${enum.ONE}
-${enum.TWO}
-${enum.THREE}
-${(enum.ONE == enum.ONE)?string("true", "false")}
-${(enum.ONE == enum.TWO)?string("true", "false")}
-${enums["org.apache.freemarker.test.templatesuite.models.BeanTestClass"]?exists?string("true", "false")}
-${obj.something}
-${obj.publicInner.x}
-${obj.publicInner.m()}
-<@assertFails message="obj.privateInner.x">${obj.privateInner.x}</@>
-<@assertFails message="obj.privateInner.m">${obj.privateInner.m()}</@>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/templates/default-object-wrapper.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/templates/default-object-wrapper.ftl b/src/test/resources/org/apache/freemarker/test/templatesuite/templates/default-object-wrapper.ftl
new file mode 100644
index 0000000..d44f26e
--- /dev/null
+++ b/src/test/resources/org/apache/freemarker/test/templatesuite/templates/default-object-wrapper.ftl
@@ -0,0 +1,60 @@
+<#--
+ 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.
+-->
+<#list array as item>
+${item}
+</#list>
+${array?size}
+${array[0]}
+${array[1]}
+<#list list as item>
+${item}
+</#list>
+${list?size}
+${list?hasContent?then('not empty', 'empty')}
+${list[0]}
+${map.key}
+${map?api.get(objKey)}
+${obj.foo}
+<#if obj.foo?exists>hasfoo<#else>nofoo</#if>
+<#if obj.baz?exists>hasbaz<#else>nobaz</#if>
+${obj.bar[0]}
+${obj.bar(0)}
+${obj.getFoo()}
+${obj.overloaded(1?int)}
+${obj.overloaded("String")}
+${resourceBundle.message}
+${resourceBundle("format", date)}
+<#assign static = statics["org.apache.freemarker.test.templatesuite.models.BeanTestClass"]>
+${static.staticMethod()}
+${static.staticOverloaded[1]}
+${static.staticOverloaded("String")}
+${static.STATIC_FINAL_FIELD}
+${static.STATIC_FIELD}
+<#assign enum = enums["org.apache.freemarker.test.templatesuite.models.EnumTestClass"]>
+${enum.ONE}
+${enum.TWO}
+${enum.THREE}
+${(enum.ONE == enum.ONE)?string("true", "false")}
+${(enum.ONE == enum.TWO)?string("true", "false")}
+${enums["org.apache.freemarker.test.templatesuite.models.BeanTestClass"]?exists?string("true", "false")}
+${obj.something}
+${obj.publicInner.x}
+${obj.publicInner.m()}
+<@assertFails message="obj.privateInner.x">${obj.privateInner.x}</@>
+<@assertFails message="obj.privateInner.m">${obj.privateInner.m()}</@>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/templates/overloaded-methods.ftl
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/templates/overloaded-methods.ftl b/src/test/resources/org/apache/freemarker/test/templatesuite/templates/overloaded-methods.ftl
index 2d85dcc..8cf116f 100644
--- a/src/test/resources/org/apache/freemarker/test/templatesuite/templates/overloaded-methods.ftl
+++ b/src/test/resources/org/apache/freemarker/test/templatesuite/templates/overloaded-methods.ftl
@@ -19,9 +19,7 @@
<@assertEquals actual=obj.mStringArrayVsListPreference(obj.javaStringList) expected="mStringArrayVsListPreference(List [a, b])" />
<@assertEquals actual=obj.mStringArrayVsListPreference(obj.javaStringArray) expected="mStringArrayVsListPreference(String[] [a, b])" />
-<#if dow>
- <@assertEquals actual=obj.mStringArrayVsListPreference(obj.javaObjectArray) expected="mStringArrayVsListPreference(List [a, b])" />
-</#if>
+<@assertEquals actual=obj.mStringArrayVsListPreference(obj.javaObjectArray) expected="mStringArrayVsListPreference(List [a, b])" />
<#-- Check if non-overloaded calls still work; they share some code with overloaded methods: -->
<@assertEquals actual=obj.mIntArrayNonOverloaded([1, 2, 3]) expected="mIntArrayNonOverloaded(int[] [1, 2, 3])" />
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/testcases.xml
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/testcases.xml b/src/test/resources/org/apache/freemarker/test/templatesuite/testcases.xml
index c573c72..e3be657 100644
--- a/src/test/resources/org/apache/freemarker/test/templatesuite/testcases.xml
+++ b/src/test/resources/org/apache/freemarker/test/templatesuite/testcases.xml
@@ -56,23 +56,10 @@ Note that for the incompatible_improvements setting you can specify a list of ve
<setting input_encoding="UTF-8" output_encoding="UTF-8" />
<testCase name="api-builtins" noOutput="true">
- <setting
- object_wrapper="DefaultObjectWrapper(3.0.0)"
- api_builtin_enabled="true"
- />
- </testCase>
- <testCase name="api-builtins[#endTN]-bw" noOutput="true">
- <setting
- object_wrapper="BeansWrapper(3.0.0)"
- api_builtin_enabled="true"
- />
+ <setting api_builtin_enabled="true" />
</testCase>
<testCase name="arithmetic" />
<testCase name="assignments" noOutput="true" />
- <testCase name="bean-maps"/>
- <testCase name="beans">
- <setting object_wrapper="beans"/>
- </testCase>
<testCase name="boolean" />
<testCase name="charset-in-header">
<setting clear_encoding_map="Y" input_encoding="ISO-8859-5" />
@@ -86,9 +73,10 @@ Note that for the incompatible_improvements setting you can specify a list of ve
<testCase name="dateformat-iso-bi" noOutput="true" />
<testCase name="dateparsing" noOutput="true" />
<testCase name="default"/>
- <testCase name="default-xmlns">
- <setting object_wrapper="beans"/>
+ <testCase name="default-object-wrapper">
+ <setting api_builtin_enabled="true" />
</testCase>
+ <testCase name="default-xmlns" />
<testCase name="encoding-builtins" />
<testCase name="escapes" />
<testCase name="hashliteral" />
@@ -158,9 +146,6 @@ Note that for the incompatible_improvements setting you can specify a list of ve
<testCase name="recover" />
<testCase name="root" />
<testCase name="setting" noOutput="true" />
- <testCase name="sequence-builtins[#endTN]-with-BeansWrapper" expected="sequence-builtins.txt">
- <setting object_wrapper="beans"/>
- </testCase>
<testCase name="sequence-builtins[#endTN]-with-SimpleTemplateModel" expected="sequence-builtins.txt">
<setting object_wrapper="org.apache.freemarker.test.util.SimpleMapAndCollectionObjectWrapper(3.0.0)" />
</testCase>
@@ -190,22 +175,12 @@ Note that for the incompatible_improvements setting you can specify a list of ve
<testCase name="variables"/>
<testCase name="whitespace-trim"/>
<testCase name="wstrip-in-header"/>
- <testCase name="xml-fragment">
- <setting object_wrapper="beans"/>
- </testCase>
- <testCase name="xmlns1">
- <setting object_wrapper="beans"/>
- </testCase>
+ <testCase name="xml-fragment" />
+ <testCase name="xmlns1" />
<testCase name="xmlns2" template="xmlns1.ftl" expected="xmlns1.txt" />
- <testCase name="xmlns3">
- <setting object_wrapper="beans"/>
- </testCase>
- <testCase name="xmlns4">
- <setting object_wrapper="beans"/>
- </testCase>
- <testCase name="xmlns5">
- <setting object_wrapper="beans"/>
- </testCase>
+ <testCase name="xmlns3" />
+ <testCase name="xmlns4" />
+ <testCase name="xmlns5" />
<testCase name="xml-ns_prefix-scope" template="xml-ns_prefix-scope-main.ftl" />
<testCase name="hashconcat"/>
<testCase name="new-defaultresolver" />
@@ -230,16 +205,10 @@ Note that for the incompatible_improvements setting you can specify a list of ve
<testCase name="number-math-builtins" noOutput="true" />
<testCase name="string-builtin-coercion" noOutput="true" />
- <testCase name="overloaded-methods[#endTN]-inc-bw" noOutput="true">
- <setting object_wrapper="org.apache.freemarker.core.model.impl.beans.BeansWrapperInc"/>
- </testCase>
- <testCase name="overloaded-methods[#endTN]-desc-bw" noOutput="true">
- <setting object_wrapper="org.apache.freemarker.core.model.impl.beans.BeansWrapperDesc"/>
- </testCase>
<testCase name="overloaded-methods[#endTN]-inc-dow" noOutput="true">
- <setting object_wrapper="org.apache.freemarker.core.model.impl.beans.DefaultObjectWrapperInc"/>
+ <setting object_wrapper="org.apache.freemarker.core.model.impl.DefaultObjectWrapperInc"/>
</testCase>
<testCase name="overloaded-methods[#endTN]-desc-dow" noOutput="true">
- <setting object_wrapper="org.apache.freemarker.core.model.impl.beans.DefaultObjectWrapperDesc"/>
+ <setting object_wrapper="org.apache.freemarker.core.model.impl.DefaultObjectWrapperDesc"/>
</testCase>
</testCases>
[08/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
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);
+ }
+ }
+ }
+ }
+ }
+
+}
[06/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/Java7MembersOnlyDefaultObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/Java7MembersOnlyDefaultObjectWrapper.java b/src/test/java/org/apache/freemarker/core/model/impl/Java7MembersOnlyDefaultObjectWrapper.java
new file mode 100644
index 0000000..9307a00
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/Java7MembersOnlyDefaultObjectWrapper.java
@@ -0,0 +1,102 @@
+/*
+ * 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.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.Version;
+
+/**
+ * A hack to keep some unit tests producing exactly the same results on Java 8 as on Java 5-7, by hiding members
+ * added after Java 7.
+ */
+public class Java7MembersOnlyDefaultObjectWrapper extends DefaultObjectWrapper {
+
+ private static final Set<String> POST_JAVA_7_MAP_METHODS = newHashSet(
+ "compute", "computeIfAbsent", "computeIfPresent",
+ "forEach", "getOrDefault", "merge", "putIfAbsent", "replace", "replaceAll");
+
+ private static final Set<String> POST_JAVA_7_ITERABLE_METHODS = newHashSet("forEach");
+ private static final Set<String> POST_JAVA_7_COLLECTION_METHODS = newHashSet("parallelStream", "removeIf", "stream");
+ private static final Set<String> POST_JAVA_7_LIST_METHODS = newHashSet("sort", "spliterator");
+
+ MethodAppearanceFineTuner POST_JAVA_7_FILTER = new MethodAppearanceFineTuner() {
+
+ @Override
+ public void process(DecisionInput in, Decision out) {
+ Method m = in.getMethod();
+ Class declCl = m.getDeclaringClass();
+ if (Map.class.isAssignableFrom(declCl)) {
+ if (POST_JAVA_7_MAP_METHODS.contains(m.getName())) {
+ hideMember(out);
+ return;
+ }
+ }
+ if (Iterable.class.isAssignableFrom(declCl)) {
+ if (POST_JAVA_7_ITERABLE_METHODS.contains(m.getName())) {
+ hideMember(out);
+ return;
+ }
+ }
+ if (Collection.class.isAssignableFrom(declCl)) {
+ if (POST_JAVA_7_COLLECTION_METHODS.contains(m.getName())) {
+ hideMember(out);
+ return;
+ }
+ }
+ if (List.class.isAssignableFrom(declCl)) {
+ if (POST_JAVA_7_LIST_METHODS.contains(m.getName())) {
+ hideMember(out);
+ return;
+ }
+ }
+ }
+
+ private void hideMember(Decision out) {
+ out.setExposeMethodAs(null);
+ out.setExposeAsProperty(null);
+ }
+
+ };
+
+ public Java7MembersOnlyDefaultObjectWrapper(Version version) {
+ super(version);
+ setMethodAppearanceFineTuner(POST_JAVA_7_FILTER);
+ }
+
+ private static <T> Set<T> newHashSet(T... items) {
+ HashSet<T> r = new HashSet<>();
+ for (T item : items) {
+ r.add(item);
+ }
+ return r;
+ }
+
+ public Java7MembersOnlyDefaultObjectWrapper() {
+ this(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/ManyObjectsOfDifferentClasses.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/ManyObjectsOfDifferentClasses.java b/src/test/java/org/apache/freemarker/core/model/impl/ManyObjectsOfDifferentClasses.java
new file mode 100644
index 0000000..44ef2f5
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/ManyObjectsOfDifferentClasses.java
@@ -0,0 +1,249 @@
+/*
+ * 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;
+
+// FMPP template source:
+// ----
+// package org.apache.freemarker.core.model.impl;
+//
+// // FMPP template source:
+// // ----
+// <#assign src><#include "ManyObjectsOfDifferentClasses.java.ftl" parse=false></#assign>
+// ${src?trim?replace('(.*\r?\n)|.+', r'// $0', 'r')}
+// // ----
+//
+// <#assign MAX_CLA = 8 - 1>
+// <#assign MAX_MET = 10 - 1>
+// <#assign MAX_PRO = 10 - 1>
+//
+// public class ManyObjectsOfDifferentClasses {
+//
+// public static final Object[] OBJECTS = new Object[] {
+// <#list 0..MAX_CLA as claI>
+// new C${claI}(),
+// </#list>
+// };
+//
+// <#list 0..MAX_CLA as claI>
+// static public class C${claI} {
+// <#list 0..MAX_PRO as proI>
+// public int getP${proI}() { return ${claI} * 1000 + ${proI}; }
+// </#list>
+// <#list 0..MAX_MET as metI>
+// public int m${metI}() { return ${claI} * 1000 + ${metI}; };
+// </#list>
+// }
+// </#list>
+//
+// }
+// ----
+
+
+public class ManyObjectsOfDifferentClasses {
+
+ public static final Object[] OBJECTS = new Object[] {
+ new C0(),
+ new C1(),
+ new C2(),
+ new C3(),
+ new C4(),
+ new C5(),
+ new C6(),
+ new C7(),
+ };
+
+ static public class C0 {
+ public int getP0() { return 0 * 1000 + 0; }
+ public int getP1() { return 0 * 1000 + 1; }
+ public int getP2() { return 0 * 1000 + 2; }
+ public int getP3() { return 0 * 1000 + 3; }
+ public int getP4() { return 0 * 1000 + 4; }
+ public int getP5() { return 0 * 1000 + 5; }
+ public int getP6() { return 0 * 1000 + 6; }
+ public int getP7() { return 0 * 1000 + 7; }
+ public int getP8() { return 0 * 1000 + 8; }
+ public int getP9() { return 0 * 1000 + 9; }
+ public int m0() { return 0 * 1000 + 0; };
+ public int m1() { return 0 * 1000 + 1; };
+ public int m2() { return 0 * 1000 + 2; };
+ public int m3() { return 0 * 1000 + 3; };
+ public int m4() { return 0 * 1000 + 4; };
+ public int m5() { return 0 * 1000 + 5; };
+ public int m6() { return 0 * 1000 + 6; };
+ public int m7() { return 0 * 1000 + 7; };
+ public int m8() { return 0 * 1000 + 8; };
+ public int m9() { return 0 * 1000 + 9; };
+ }
+ static public class C1 {
+ public int getP0() { return 1 * 1000 + 0; }
+ public int getP1() { return 1 * 1000 + 1; }
+ public int getP2() { return 1 * 1000 + 2; }
+ public int getP3() { return 1 * 1000 + 3; }
+ public int getP4() { return 1 * 1000 + 4; }
+ public int getP5() { return 1 * 1000 + 5; }
+ public int getP6() { return 1 * 1000 + 6; }
+ public int getP7() { return 1 * 1000 + 7; }
+ public int getP8() { return 1 * 1000 + 8; }
+ public int getP9() { return 1 * 1000 + 9; }
+ public int m0() { return 1 * 1000 + 0; };
+ public int m1() { return 1 * 1000 + 1; };
+ public int m2() { return 1 * 1000 + 2; };
+ public int m3() { return 1 * 1000 + 3; };
+ public int m4() { return 1 * 1000 + 4; };
+ public int m5() { return 1 * 1000 + 5; };
+ public int m6() { return 1 * 1000 + 6; };
+ public int m7() { return 1 * 1000 + 7; };
+ public int m8() { return 1 * 1000 + 8; };
+ public int m9() { return 1 * 1000 + 9; };
+ }
+ static public class C2 {
+ public int getP0() { return 2 * 1000 + 0; }
+ public int getP1() { return 2 * 1000 + 1; }
+ public int getP2() { return 2 * 1000 + 2; }
+ public int getP3() { return 2 * 1000 + 3; }
+ public int getP4() { return 2 * 1000 + 4; }
+ public int getP5() { return 2 * 1000 + 5; }
+ public int getP6() { return 2 * 1000 + 6; }
+ public int getP7() { return 2 * 1000 + 7; }
+ public int getP8() { return 2 * 1000 + 8; }
+ public int getP9() { return 2 * 1000 + 9; }
+ public int m0() { return 2 * 1000 + 0; };
+ public int m1() { return 2 * 1000 + 1; };
+ public int m2() { return 2 * 1000 + 2; };
+ public int m3() { return 2 * 1000 + 3; };
+ public int m4() { return 2 * 1000 + 4; };
+ public int m5() { return 2 * 1000 + 5; };
+ public int m6() { return 2 * 1000 + 6; };
+ public int m7() { return 2 * 1000 + 7; };
+ public int m8() { return 2 * 1000 + 8; };
+ public int m9() { return 2 * 1000 + 9; };
+ }
+ static public class C3 {
+ public int getP0() { return 3 * 1000 + 0; }
+ public int getP1() { return 3 * 1000 + 1; }
+ public int getP2() { return 3 * 1000 + 2; }
+ public int getP3() { return 3 * 1000 + 3; }
+ public int getP4() { return 3 * 1000 + 4; }
+ public int getP5() { return 3 * 1000 + 5; }
+ public int getP6() { return 3 * 1000 + 6; }
+ public int getP7() { return 3 * 1000 + 7; }
+ public int getP8() { return 3 * 1000 + 8; }
+ public int getP9() { return 3 * 1000 + 9; }
+ public int m0() { return 3 * 1000 + 0; };
+ public int m1() { return 3 * 1000 + 1; };
+ public int m2() { return 3 * 1000 + 2; };
+ public int m3() { return 3 * 1000 + 3; };
+ public int m4() { return 3 * 1000 + 4; };
+ public int m5() { return 3 * 1000 + 5; };
+ public int m6() { return 3 * 1000 + 6; };
+ public int m7() { return 3 * 1000 + 7; };
+ public int m8() { return 3 * 1000 + 8; };
+ public int m9() { return 3 * 1000 + 9; };
+ }
+ static public class C4 {
+ public int getP0() { return 4 * 1000 + 0; }
+ public int getP1() { return 4 * 1000 + 1; }
+ public int getP2() { return 4 * 1000 + 2; }
+ public int getP3() { return 4 * 1000 + 3; }
+ public int getP4() { return 4 * 1000 + 4; }
+ public int getP5() { return 4 * 1000 + 5; }
+ public int getP6() { return 4 * 1000 + 6; }
+ public int getP7() { return 4 * 1000 + 7; }
+ public int getP8() { return 4 * 1000 + 8; }
+ public int getP9() { return 4 * 1000 + 9; }
+ public int m0() { return 4 * 1000 + 0; };
+ public int m1() { return 4 * 1000 + 1; };
+ public int m2() { return 4 * 1000 + 2; };
+ public int m3() { return 4 * 1000 + 3; };
+ public int m4() { return 4 * 1000 + 4; };
+ public int m5() { return 4 * 1000 + 5; };
+ public int m6() { return 4 * 1000 + 6; };
+ public int m7() { return 4 * 1000 + 7; };
+ public int m8() { return 4 * 1000 + 8; };
+ public int m9() { return 4 * 1000 + 9; };
+ }
+ static public class C5 {
+ public int getP0() { return 5 * 1000 + 0; }
+ public int getP1() { return 5 * 1000 + 1; }
+ public int getP2() { return 5 * 1000 + 2; }
+ public int getP3() { return 5 * 1000 + 3; }
+ public int getP4() { return 5 * 1000 + 4; }
+ public int getP5() { return 5 * 1000 + 5; }
+ public int getP6() { return 5 * 1000 + 6; }
+ public int getP7() { return 5 * 1000 + 7; }
+ public int getP8() { return 5 * 1000 + 8; }
+ public int getP9() { return 5 * 1000 + 9; }
+ public int m0() { return 5 * 1000 + 0; };
+ public int m1() { return 5 * 1000 + 1; };
+ public int m2() { return 5 * 1000 + 2; };
+ public int m3() { return 5 * 1000 + 3; };
+ public int m4() { return 5 * 1000 + 4; };
+ public int m5() { return 5 * 1000 + 5; };
+ public int m6() { return 5 * 1000 + 6; };
+ public int m7() { return 5 * 1000 + 7; };
+ public int m8() { return 5 * 1000 + 8; };
+ public int m9() { return 5 * 1000 + 9; };
+ }
+ static public class C6 {
+ public int getP0() { return 6 * 1000 + 0; }
+ public int getP1() { return 6 * 1000 + 1; }
+ public int getP2() { return 6 * 1000 + 2; }
+ public int getP3() { return 6 * 1000 + 3; }
+ public int getP4() { return 6 * 1000 + 4; }
+ public int getP5() { return 6 * 1000 + 5; }
+ public int getP6() { return 6 * 1000 + 6; }
+ public int getP7() { return 6 * 1000 + 7; }
+ public int getP8() { return 6 * 1000 + 8; }
+ public int getP9() { return 6 * 1000 + 9; }
+ public int m0() { return 6 * 1000 + 0; };
+ public int m1() { return 6 * 1000 + 1; };
+ public int m2() { return 6 * 1000 + 2; };
+ public int m3() { return 6 * 1000 + 3; };
+ public int m4() { return 6 * 1000 + 4; };
+ public int m5() { return 6 * 1000 + 5; };
+ public int m6() { return 6 * 1000 + 6; };
+ public int m7() { return 6 * 1000 + 7; };
+ public int m8() { return 6 * 1000 + 8; };
+ public int m9() { return 6 * 1000 + 9; };
+ }
+ static public class C7 {
+ public int getP0() { return 7 * 1000 + 0; }
+ public int getP1() { return 7 * 1000 + 1; }
+ public int getP2() { return 7 * 1000 + 2; }
+ public int getP3() { return 7 * 1000 + 3; }
+ public int getP4() { return 7 * 1000 + 4; }
+ public int getP5() { return 7 * 1000 + 5; }
+ public int getP6() { return 7 * 1000 + 6; }
+ public int getP7() { return 7 * 1000 + 7; }
+ public int getP8() { return 7 * 1000 + 8; }
+ public int getP9() { return 7 * 1000 + 9; }
+ public int m0() { return 7 * 1000 + 0; };
+ public int m1() { return 7 * 1000 + 1; };
+ public int m2() { return 7 * 1000 + 2; };
+ public int m3() { return 7 * 1000 + 3; };
+ public int m4() { return 7 * 1000 + 4; };
+ public int m5() { return 7 * 1000 + 5; };
+ public int m6() { return 7 * 1000 + 6; };
+ public int m7() { return 7 * 1000 + 7; };
+ public int m8() { return 7 * 1000 + 8; };
+ public int m9() { return 7 * 1000 + 9; };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/ManyStaticsOfDifferentClasses.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/ManyStaticsOfDifferentClasses.java b/src/test/java/org/apache/freemarker/core/model/impl/ManyStaticsOfDifferentClasses.java
new file mode 100644
index 0000000..a3f6f36
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/ManyStaticsOfDifferentClasses.java
@@ -0,0 +1,236 @@
+/*
+ * 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;
+
+// FMPP template source:
+// ----
+// package org.apache.freemarker.core.model.impl;
+//
+// // FMPP template source:
+// // ----
+// <#assign src><#include "ManyStaticsOfDifferentClasses.java.ftl" parse=false></#assign>
+// ${src?trim?replace('(.*\r?\n)|.+', r'// $0', 'r')}
+// // ----
+//
+// <#assign MAX_CLA = 8 - 1>
+// <#assign MAX_MET = 10 - 1>
+// <#assign MAX_PRO = 10 - 1>
+//
+// public class ManyStaticsOfDifferentClasses {
+//
+// private ManyStaticsOfDifferentClasses() { }
+//
+// <#list 0..MAX_CLA as claI>
+// static public class C${claI} {
+// <#list 0..MAX_PRO as proI>
+// public static int p${proI} = ${claI} * 1000 + ${proI};
+// </#list>
+// <#list 0..MAX_MET as metI>
+// public static int m${metI}() { return ${claI} * 1000 + ${metI}; };
+// </#list>
+// }
+// </#list>
+//
+// }
+// ----
+
+
+public class ManyStaticsOfDifferentClasses {
+
+ private ManyStaticsOfDifferentClasses() { }
+
+ static public class C0 {
+ public static int p0 = 0 * 1000 + 0;
+ public static int p1 = 0 * 1000 + 1;
+ public static int p2 = 0 * 1000 + 2;
+ public static int p3 = 0 * 1000 + 3;
+ public static int p4 = 0 * 1000 + 4;
+ public static int p5 = 0 * 1000 + 5;
+ public static int p6 = 0 * 1000 + 6;
+ public static int p7 = 0 * 1000 + 7;
+ public static int p8 = 0 * 1000 + 8;
+ public static int p9 = 0 * 1000 + 9;
+ public static int m0() { return 0 * 1000 + 0; };
+ public static int m1() { return 0 * 1000 + 1; };
+ public static int m2() { return 0 * 1000 + 2; };
+ public static int m3() { return 0 * 1000 + 3; };
+ public static int m4() { return 0 * 1000 + 4; };
+ public static int m5() { return 0 * 1000 + 5; };
+ public static int m6() { return 0 * 1000 + 6; };
+ public static int m7() { return 0 * 1000 + 7; };
+ public static int m8() { return 0 * 1000 + 8; };
+ public static int m9() { return 0 * 1000 + 9; };
+ }
+ static public class C1 {
+ public static int p0 = 1 * 1000 + 0;
+ public static int p1 = 1 * 1000 + 1;
+ public static int p2 = 1 * 1000 + 2;
+ public static int p3 = 1 * 1000 + 3;
+ public static int p4 = 1 * 1000 + 4;
+ public static int p5 = 1 * 1000 + 5;
+ public static int p6 = 1 * 1000 + 6;
+ public static int p7 = 1 * 1000 + 7;
+ public static int p8 = 1 * 1000 + 8;
+ public static int p9 = 1 * 1000 + 9;
+ public static int m0() { return 1 * 1000 + 0; };
+ public static int m1() { return 1 * 1000 + 1; };
+ public static int m2() { return 1 * 1000 + 2; };
+ public static int m3() { return 1 * 1000 + 3; };
+ public static int m4() { return 1 * 1000 + 4; };
+ public static int m5() { return 1 * 1000 + 5; };
+ public static int m6() { return 1 * 1000 + 6; };
+ public static int m7() { return 1 * 1000 + 7; };
+ public static int m8() { return 1 * 1000 + 8; };
+ public static int m9() { return 1 * 1000 + 9; };
+ }
+ static public class C2 {
+ public static int p0 = 2 * 1000 + 0;
+ public static int p1 = 2 * 1000 + 1;
+ public static int p2 = 2 * 1000 + 2;
+ public static int p3 = 2 * 1000 + 3;
+ public static int p4 = 2 * 1000 + 4;
+ public static int p5 = 2 * 1000 + 5;
+ public static int p6 = 2 * 1000 + 6;
+ public static int p7 = 2 * 1000 + 7;
+ public static int p8 = 2 * 1000 + 8;
+ public static int p9 = 2 * 1000 + 9;
+ public static int m0() { return 2 * 1000 + 0; };
+ public static int m1() { return 2 * 1000 + 1; };
+ public static int m2() { return 2 * 1000 + 2; };
+ public static int m3() { return 2 * 1000 + 3; };
+ public static int m4() { return 2 * 1000 + 4; };
+ public static int m5() { return 2 * 1000 + 5; };
+ public static int m6() { return 2 * 1000 + 6; };
+ public static int m7() { return 2 * 1000 + 7; };
+ public static int m8() { return 2 * 1000 + 8; };
+ public static int m9() { return 2 * 1000 + 9; };
+ }
+ static public class C3 {
+ public static int p0 = 3 * 1000 + 0;
+ public static int p1 = 3 * 1000 + 1;
+ public static int p2 = 3 * 1000 + 2;
+ public static int p3 = 3 * 1000 + 3;
+ public static int p4 = 3 * 1000 + 4;
+ public static int p5 = 3 * 1000 + 5;
+ public static int p6 = 3 * 1000 + 6;
+ public static int p7 = 3 * 1000 + 7;
+ public static int p8 = 3 * 1000 + 8;
+ public static int p9 = 3 * 1000 + 9;
+ public static int m0() { return 3 * 1000 + 0; };
+ public static int m1() { return 3 * 1000 + 1; };
+ public static int m2() { return 3 * 1000 + 2; };
+ public static int m3() { return 3 * 1000 + 3; };
+ public static int m4() { return 3 * 1000 + 4; };
+ public static int m5() { return 3 * 1000 + 5; };
+ public static int m6() { return 3 * 1000 + 6; };
+ public static int m7() { return 3 * 1000 + 7; };
+ public static int m8() { return 3 * 1000 + 8; };
+ public static int m9() { return 3 * 1000 + 9; };
+ }
+ static public class C4 {
+ public static int p0 = 4 * 1000 + 0;
+ public static int p1 = 4 * 1000 + 1;
+ public static int p2 = 4 * 1000 + 2;
+ public static int p3 = 4 * 1000 + 3;
+ public static int p4 = 4 * 1000 + 4;
+ public static int p5 = 4 * 1000 + 5;
+ public static int p6 = 4 * 1000 + 6;
+ public static int p7 = 4 * 1000 + 7;
+ public static int p8 = 4 * 1000 + 8;
+ public static int p9 = 4 * 1000 + 9;
+ public static int m0() { return 4 * 1000 + 0; };
+ public static int m1() { return 4 * 1000 + 1; };
+ public static int m2() { return 4 * 1000 + 2; };
+ public static int m3() { return 4 * 1000 + 3; };
+ public static int m4() { return 4 * 1000 + 4; };
+ public static int m5() { return 4 * 1000 + 5; };
+ public static int m6() { return 4 * 1000 + 6; };
+ public static int m7() { return 4 * 1000 + 7; };
+ public static int m8() { return 4 * 1000 + 8; };
+ public static int m9() { return 4 * 1000 + 9; };
+ }
+ static public class C5 {
+ public static int p0 = 5 * 1000 + 0;
+ public static int p1 = 5 * 1000 + 1;
+ public static int p2 = 5 * 1000 + 2;
+ public static int p3 = 5 * 1000 + 3;
+ public static int p4 = 5 * 1000 + 4;
+ public static int p5 = 5 * 1000 + 5;
+ public static int p6 = 5 * 1000 + 6;
+ public static int p7 = 5 * 1000 + 7;
+ public static int p8 = 5 * 1000 + 8;
+ public static int p9 = 5 * 1000 + 9;
+ public static int m0() { return 5 * 1000 + 0; };
+ public static int m1() { return 5 * 1000 + 1; };
+ public static int m2() { return 5 * 1000 + 2; };
+ public static int m3() { return 5 * 1000 + 3; };
+ public static int m4() { return 5 * 1000 + 4; };
+ public static int m5() { return 5 * 1000 + 5; };
+ public static int m6() { return 5 * 1000 + 6; };
+ public static int m7() { return 5 * 1000 + 7; };
+ public static int m8() { return 5 * 1000 + 8; };
+ public static int m9() { return 5 * 1000 + 9; };
+ }
+ static public class C6 {
+ public static int p0 = 6 * 1000 + 0;
+ public static int p1 = 6 * 1000 + 1;
+ public static int p2 = 6 * 1000 + 2;
+ public static int p3 = 6 * 1000 + 3;
+ public static int p4 = 6 * 1000 + 4;
+ public static int p5 = 6 * 1000 + 5;
+ public static int p6 = 6 * 1000 + 6;
+ public static int p7 = 6 * 1000 + 7;
+ public static int p8 = 6 * 1000 + 8;
+ public static int p9 = 6 * 1000 + 9;
+ public static int m0() { return 6 * 1000 + 0; };
+ public static int m1() { return 6 * 1000 + 1; };
+ public static int m2() { return 6 * 1000 + 2; };
+ public static int m3() { return 6 * 1000 + 3; };
+ public static int m4() { return 6 * 1000 + 4; };
+ public static int m5() { return 6 * 1000 + 5; };
+ public static int m6() { return 6 * 1000 + 6; };
+ public static int m7() { return 6 * 1000 + 7; };
+ public static int m8() { return 6 * 1000 + 8; };
+ public static int m9() { return 6 * 1000 + 9; };
+ }
+ static public class C7 {
+ public static int p0 = 7 * 1000 + 0;
+ public static int p1 = 7 * 1000 + 1;
+ public static int p2 = 7 * 1000 + 2;
+ public static int p3 = 7 * 1000 + 3;
+ public static int p4 = 7 * 1000 + 4;
+ public static int p5 = 7 * 1000 + 5;
+ public static int p6 = 7 * 1000 + 6;
+ public static int p7 = 7 * 1000 + 7;
+ public static int p8 = 7 * 1000 + 8;
+ public static int p9 = 7 * 1000 + 9;
+ public static int m0() { return 7 * 1000 + 0; };
+ public static int m1() { return 7 * 1000 + 1; };
+ public static int m2() { return 7 * 1000 + 2; };
+ public static int m3() { return 7 * 1000 + 3; };
+ public static int m4() { return 7 * 1000 + 4; };
+ public static int m5() { return 7 * 1000 + 5; };
+ public static int m6() { return 7 * 1000 + 6; };
+ public static int m7() { return 7 * 1000 + 7; };
+ public static int m8() { return 7 * 1000 + 8; };
+ public static int m9() { return 7 * 1000 + 9; };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/MiscNumericalOperationsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/MiscNumericalOperationsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/MiscNumericalOperationsTest.java
new file mode 100644
index 0000000..148211e
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/MiscNumericalOperationsTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.junit.Assert;
+
+import junit.framework.TestCase;
+
+public class MiscNumericalOperationsTest extends TestCase {
+
+ public MiscNumericalOperationsTest(String name) {
+ super(name);
+ }
+
+ public void testForceUnwrappedNumberToType() {
+ // Usual type to to all other types:
+ Double n = Double.valueOf(123.75);
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Short.class), Short.valueOf(n.shortValue()));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Short.TYPE), Short.valueOf(n.shortValue()));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Byte.class), Byte.valueOf(n.byteValue()));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Byte.TYPE), Byte.valueOf(n.byteValue()));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Integer.class), Integer.valueOf(n.intValue()));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Integer.TYPE), Integer.valueOf(n.intValue()));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Long.class), Long.valueOf(n.longValue()));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Long.TYPE), Long.valueOf(n.longValue()));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Float.class), Float.valueOf(n.floatValue()));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Float.TYPE), Float.valueOf(n.floatValue()));
+ assertTrue(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Double.class) == n);
+ assertTrue(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Double.TYPE) == n);
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, BigInteger.class), new BigInteger("123"));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(n, BigDecimal.class), new BigDecimal("123.75"));
+
+ // Cases of conversion to BigDecimal:
+ BigDecimal bd = new BigDecimal("123");
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(new BigInteger("123"), BigDecimal.class), bd);
+ assertTrue(DefaultObjectWrapper.forceUnwrappedNumberToType(bd, BigDecimal.class) == bd);
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(Long.valueOf(123), BigDecimal.class), bd);
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(Double.valueOf(123), BigDecimal.class), bd);
+
+ // Cases of conversion to BigInteger:
+ BigInteger bi = new BigInteger("123");
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(new BigDecimal("123.6"), BigInteger.class), bi);
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(
+ new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal("123")), BigInteger.class), bi);
+ assertTrue(DefaultObjectWrapper.forceUnwrappedNumberToType(bi, BigInteger.class) == bi);
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(Long.valueOf(123), BigInteger.class), bi);
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(Double.valueOf(123.6), BigInteger.class), bi);
+
+ assertTrue(DefaultObjectWrapper.forceUnwrappedNumberToType(n, Number.class) == n);
+ assertNull(DefaultObjectWrapper.forceUnwrappedNumberToType(n, RationalNumber.class));
+ RationalNumber r = new RationalNumber(1, 2);
+ assertTrue(DefaultObjectWrapper.forceUnwrappedNumberToType(r, RationalNumber.class) == r);
+ assertTrue(DefaultObjectWrapper.forceUnwrappedNumberToType(r, Number.class) == r);
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(r, Double.class), Double.valueOf(0.5));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(r, BigDecimal.class), new BigDecimal("0.5"));
+ assertEquals(DefaultObjectWrapper.forceUnwrappedNumberToType(r, BigInteger.class), BigInteger.ZERO);
+ }
+
+ @SuppressWarnings("boxing")
+ public void testForceNumberArgumentsToParameterTypes() {
+ OverloadedMethodsSubset oms = new OverloadedFixArgsMethods();
+ Class[] paramTypes = new Class[] { Short.TYPE, Short.class, Double.TYPE, BigDecimal.class, BigInteger.class };
+ Object[] args;
+
+ args = newArgs();
+ oms.forceNumberArgumentsToParameterTypes(args, paramTypes, new int[] { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF });
+ Assert.assertArrayEquals(
+ args,
+ new Object[] { (short) 123, (short) 123, 123.75, new BigDecimal("123.75"), BigInteger.valueOf(123) });
+
+ args = newArgs();
+ oms.forceNumberArgumentsToParameterTypes(args, paramTypes, new int[] { 0, 0, 0, 0, 0 });
+ Assert.assertArrayEquals(args, newArgs());
+
+ args = newArgs();
+ oms.forceNumberArgumentsToParameterTypes(args, paramTypes, new int[] { 8, 8, 8, 8, 8 });
+ Assert.assertArrayEquals(args, newArgs());
+
+ args = newArgs();
+ oms.forceNumberArgumentsToParameterTypes(args, paramTypes, new int[] { 0xFFFF, 0, 0xFFFF, 0, 0xFFFF });
+ Assert.assertArrayEquals(
+ args,
+ new Object[] { (short) 123, 123.75, 123.75, 123.75, BigInteger.valueOf(123) });
+ }
+
+ @SuppressWarnings("boxing")
+ private Object[] newArgs() {
+ return new Object[] { 123.75, 123.75, 123.75, 123.75, 123.75 };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/ModelAPINewInstanceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/ModelAPINewInstanceTest.java b/src/test/java/org/apache/freemarker/core/model/impl/ModelAPINewInstanceTest.java
new file mode 100644
index 0000000..7e66b72
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/ModelAPINewInstanceTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.test.util.TestUtil;
+
+import junit.framework.TestCase;
+
+public class ModelAPINewInstanceTest extends TestCase {
+
+ private DefaultObjectWrapper ow = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+
+ public ModelAPINewInstanceTest(String name) {
+ super(name);
+ }
+
+ @SuppressWarnings("boxing")
+ public void test() throws Exception {
+ testClassConstructors("");
+ testClassConstructors("int... [1]", 1);
+ testClassConstructors("int 1, int 2", 1, 2);
+ testClassConstructors("int... [1, 2, 3]", 1, 2, 3);
+ testClassConstructors("int... [1, 2, 3]", 1, (byte) 2, (short) 3);
+ try {
+ testClassConstructors("int... [1, 2, 3]", 1, 2, (long) 3);
+ fail();
+ } catch (NoSuchMethodException e) {
+ // Expected
+ }
+
+ testClassConstructors("int 1, int 2", (byte) 1, (short) 2);
+ testClassConstructors("double 1.0, double 2.0", (long) 1, (short) 2);
+ testClassConstructors("double 1.0, double 2.0", 1, 2f);
+ testClassConstructors("Integer null, Integer null", null, null);
+ testClassConstructors("Integer null, Integer 1", null, 1);
+ testClassConstructors("int 1, String s", 1, "s");
+ testClassConstructors("int 1, String null", 1, null);
+ testClassConstructors("Object null, Object s", null, "s");
+ testClassConstructors("Object 1.0, Object s", 1f, "s");
+
+ testClassConstructors("Object s, int... [1, 2]", "s", 1, 2);
+ testClassConstructors("Object s, int... []", "s");
+
+ testClassConstructors2("int 1, int 2", (byte) 1, (short) 2);
+ try {
+ testClassConstructors2("int 1, int 2", 1, 2L);
+ fail();
+ } catch (NoSuchMethodException e) {
+ // Expected
+ }
+ try {
+ testClassConstructors2("", "", "");
+ fail();
+ } catch (NoSuchMethodException e) {
+ // Expected
+ }
+ try {
+ testClassConstructors2("int 1", 1);
+ fail();
+ } catch (NoSuchMethodException e) {
+ // Expected
+ }
+ try {
+ testClassConstructors2("");
+ fail();
+ } catch (NoSuchMethodException e) {
+ // Expected
+ }
+ }
+
+ private void testClassConstructors(String expected, Object... args) throws Exception {
+ testCall(expected, Constructors.class, args);
+ }
+
+ private void testClassConstructors2(String expected, Object... args) throws Exception {
+ testCall(expected, Constructors2.class, args);
+ }
+
+ private void testCall(String expected, Class cl, Object... args) throws Exception {
+ Object obj = _ModelAPI.newInstance(cl, args, ow);
+ assertEquals(expected, obj.toString());
+ }
+
+ public static class Constructors {
+ private final String s;
+
+ public Constructors() { s = ""; }
+
+ public Constructors(int x, int y) { s = "int " + x + ", int " + y; }
+ public Constructors(int x, String y) { s = "int " + x + ", String " + y; }
+ public Constructors(int x, long y) { s = "int " + x + ", long " + y; }
+ public Constructors(double x, double y) { s = "double " + x + ", double " + y; }
+ public Constructors(Integer x, Integer y) { s = "Integer " + x + ", Integer " + y; }
+ public Constructors(Object x, Object y) { s = "Object " + x + ", Object " + y; }
+
+ public Constructors(int... xs) { s = "int... " + TestUtil.arrayToString(xs); }
+ public Constructors(Object x, int... ys) { s = "Object " + x + ", int... " + TestUtil.arrayToString(ys); }
+
+ @Override
+ public String toString() {
+ return s;
+ }
+ }
+
+ public static class Constructors2 {
+ private final String s;
+
+ public Constructors2(int x, int y) { s = "int " + x + ", int " + y; }
+
+ @Override
+ public String toString() {
+ return s;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/ModelCacheTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/ModelCacheTest.java b/src/test/java/org/apache/freemarker/core/model/impl/ModelCacheTest.java
new file mode 100644
index 0000000..4ad3c0e
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/ModelCacheTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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 org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ModelCacheTest {
+
+ @Test
+ public void modelCacheOff() throws Exception {
+ DefaultObjectWrapper ow = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ assertFalse(ow.getUseModelCache()); // default is off
+
+ String s = "foo";
+ assertNotSame(ow.wrap(s), ow.wrap(s));
+
+ C c = new C();
+ assertNotSame(ow.wrap(c), ow.wrap(c));
+ }
+
+ @Test
+ public void modelCacheOn() throws Exception {
+ DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
+ ow.setUseModelCache(true);
+ assertTrue(ow.getUseModelCache());
+
+ TestBean obj = new TestBean();
+ assertSame(ow.wrap(obj), ow.wrap(obj));
+
+ C c = new C();
+ TemplateModel wrappedC = ow.wrap(c);
+ assertSame(wrappedC, ow.wrap(c));
+
+ ow.clearClassIntrospecitonCache();
+ assertNotSame(wrappedC, ow.wrap(c));
+ assertSame(ow.wrap(c), ow.wrap(c));
+ }
+
+ static public class C { }
+
+ public static class TestBean {
+ //
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtilTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtilTest.java b/src/test/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtilTest.java
new file mode 100644
index 0000000..646da00
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtilTest.java
@@ -0,0 +1,585 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+
+@SuppressWarnings("boxing")
+public class OverloadedNumberUtilTest extends TestCase {
+
+ public OverloadedNumberUtilTest(String name) {
+ super(name);
+ }
+
+ public void testIntegerCoercions() {
+ cipEqu(Byte.valueOf(Byte.MAX_VALUE));
+ cipEqu(Byte.valueOf((byte) 0));
+ cipEqu(Byte.valueOf(Byte.MIN_VALUE));
+
+ cipEqu(Short.valueOf(Byte.MAX_VALUE),
+ new OverloadedNumberUtil.ShortOrByte((short) Byte.MAX_VALUE, Byte.MAX_VALUE));
+ cipEqu(Short.valueOf((short) 0),
+ new OverloadedNumberUtil.ShortOrByte((short) 0, (byte) 0));
+ cipEqu(Short.valueOf(Byte.MIN_VALUE),
+ new OverloadedNumberUtil.ShortOrByte((short) Byte.MIN_VALUE, Byte.MIN_VALUE));
+
+ cipEqu(Short.valueOf((short) (Byte.MAX_VALUE + 1)));
+ cipEqu(Short.valueOf((short) (Byte.MIN_VALUE - 1)));
+ cipEqu(Short.valueOf(Short.MAX_VALUE));
+ cipEqu(Short.valueOf(Short.MIN_VALUE));
+
+ cipEqu(Integer.valueOf(Byte.MAX_VALUE),
+ new OverloadedNumberUtil.IntegerOrByte((int) Byte.MAX_VALUE, Byte.MAX_VALUE));
+ cipEqu(Integer.valueOf(0),
+ new OverloadedNumberUtil.IntegerOrByte(0, (byte) 0));
+ cipEqu(Integer.valueOf(Byte.MIN_VALUE),
+ new OverloadedNumberUtil.IntegerOrByte((int) Byte.MIN_VALUE, Byte.MIN_VALUE));
+
+ cipEqu(Integer.valueOf(Byte.MAX_VALUE + 1),
+ new OverloadedNumberUtil.IntegerOrShort(Byte.MAX_VALUE + 1, (short) (Byte.MAX_VALUE + 1)));
+ cipEqu(Integer.valueOf(Byte.MIN_VALUE - 1),
+ new OverloadedNumberUtil.IntegerOrShort(Byte.MIN_VALUE - 1, (short) (Byte.MIN_VALUE - 1)));
+ cipEqu(Integer.valueOf(Short.MAX_VALUE),
+ new OverloadedNumberUtil.IntegerOrShort((int) Short.MAX_VALUE, Short.MAX_VALUE));
+ cipEqu(Integer.valueOf(Short.MIN_VALUE),
+ new OverloadedNumberUtil.IntegerOrShort((int) Short.MIN_VALUE, Short.MIN_VALUE));
+
+ cipEqu(Integer.valueOf(Short.MAX_VALUE + 1));
+ cipEqu(Integer.valueOf(Short.MIN_VALUE - 1));
+ cipEqu(Integer.valueOf(Integer.MAX_VALUE));
+ cipEqu(Integer.valueOf(Integer.MIN_VALUE));
+
+ cipEqu(Long.valueOf(Byte.MAX_VALUE),
+ new OverloadedNumberUtil.LongOrByte((long) Byte.MAX_VALUE, Byte.MAX_VALUE));
+ cipEqu(Long.valueOf(0),
+ new OverloadedNumberUtil.LongOrByte((long) 0, (byte) 0));
+ cipEqu(Long.valueOf(Byte.MIN_VALUE),
+ new OverloadedNumberUtil.LongOrByte((long) Byte.MIN_VALUE, Byte.MIN_VALUE));
+
+ cipEqu(Long.valueOf(Byte.MAX_VALUE + 1),
+ new OverloadedNumberUtil.LongOrShort((long) (Byte.MAX_VALUE + 1), (short) (Byte.MAX_VALUE + 1)));
+ cipEqu(Long.valueOf(Byte.MIN_VALUE - 1),
+ new OverloadedNumberUtil.LongOrShort((long) (Byte.MIN_VALUE - 1), (short) (Byte.MIN_VALUE - 1)));
+ cipEqu(Long.valueOf(Short.MAX_VALUE),
+ new OverloadedNumberUtil.LongOrShort((long) Short.MAX_VALUE, Short.MAX_VALUE));
+ cipEqu(Long.valueOf(Short.MIN_VALUE),
+ new OverloadedNumberUtil.LongOrShort((long) Short.MIN_VALUE, Short.MIN_VALUE));
+
+ cipEqu(Long.valueOf(Short.MAX_VALUE + 1),
+ new OverloadedNumberUtil.LongOrInteger((long) Short.MAX_VALUE + 1, Short.MAX_VALUE + 1));
+ cipEqu(Long.valueOf(Short.MIN_VALUE - 1),
+ new OverloadedNumberUtil.LongOrInteger((long) Short.MIN_VALUE - 1, Short.MIN_VALUE - 1));
+ cipEqu(Long.valueOf(Integer.MAX_VALUE),
+ new OverloadedNumberUtil.LongOrInteger((long) Integer.MAX_VALUE, Integer.MAX_VALUE));
+ cipEqu(Long.valueOf(Integer.MIN_VALUE),
+ new OverloadedNumberUtil.LongOrInteger((long) Integer.MIN_VALUE, Integer.MIN_VALUE));
+
+ cipEqu(Long.valueOf(Integer.MAX_VALUE + 1L));
+ cipEqu(Long.valueOf(Integer.MIN_VALUE - 1L));
+ cipEqu(Long.valueOf(Long.MAX_VALUE));
+ cipEqu(Long.valueOf(Long.MIN_VALUE));
+ }
+
+ public void testIntegerNoCoercions() {
+ cipEqu(Integer.valueOf(Byte.MAX_VALUE), Integer.valueOf(Byte.MAX_VALUE), 0);
+ cipEqu(Integer.valueOf(0), Integer.valueOf(0), 0);
+ cipEqu(Integer.valueOf(Byte.MIN_VALUE), Integer.valueOf(Byte.MIN_VALUE), 0);
+ }
+
+ public void testIntegerLimitedCoercions() {
+ cipEqu(Integer.valueOf(Byte.MAX_VALUE), Integer.valueOf(Byte.MAX_VALUE), TypeFlags.INTEGER);
+ cipEqu(Integer.valueOf(0), Integer.valueOf(0), TypeFlags.INTEGER);
+ cipEqu(Integer.valueOf(Byte.MIN_VALUE), Integer.valueOf(Byte.MIN_VALUE), TypeFlags.INTEGER);
+
+ cipEqu(Long.valueOf(Integer.MAX_VALUE + 1L), Long.valueOf(Integer.MAX_VALUE + 1L), TypeFlags.INTEGER);
+
+ for (int n = -1; n < 2; n++) {
+ final Long longN = Long.valueOf(n);
+ cipEqu(longN, new OverloadedNumberUtil.LongOrInteger(longN, n), TypeFlags.INTEGER);
+ cipEqu(longN, new OverloadedNumberUtil.LongOrShort(longN, (short) n), TypeFlags.SHORT);
+ cipEqu(longN, new OverloadedNumberUtil.LongOrByte(longN, (byte) n), TypeFlags.BYTE);
+ cipEqu(longN, new OverloadedNumberUtil.LongOrShort(longN, (short) n),
+ TypeFlags.SHORT | TypeFlags.INTEGER);
+ }
+ }
+
+ public void testBigDecimalCoercions() {
+ cipEqu(new BigDecimal(123), new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal(123)));
+ cipEqu(new BigDecimal(123), new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal(123)),
+ TypeFlags.DOUBLE | TypeFlags.INTEGER);
+ cipEqu(new BigDecimal(123), TypeFlags.INTEGER);
+ cipEqu(new BigDecimal(123), TypeFlags.INTEGER | TypeFlags.LONG);
+ cipEqu(new BigDecimal(123), TypeFlags.DOUBLE);
+ cipEqu(new BigDecimal(123), TypeFlags.DOUBLE | TypeFlags.FLOAT);
+
+ cipEqu(new BigDecimal(123.5));
+ // Not wasting time with check if it's a whole number if we only have integer-only or non-integer-only targets:
+ cipEqu(new BigDecimal(123.5), TypeFlags.INTEGER | TypeFlags.LONG);
+ cipEqu(new BigDecimal(123.5), TypeFlags.DOUBLE | TypeFlags.FLOAT);
+
+ cipEqu(new BigDecimal("0.01"));
+ cipEqu(new BigDecimal("-0.01"));
+ cipEqu(BigDecimal.ZERO, new OverloadedNumberUtil.IntegerBigDecimal(BigDecimal.ZERO));
+ }
+
+ public void testUnknownNumberCoercion() {
+ cipEqu(new RationalNumber(2, 3));
+ }
+
+ @SuppressWarnings("boxing")
+ public void testDoubleCoercion() {
+ cipEqu(Double.valueOf(1.5), new OverloadedNumberUtil.DoubleOrFloat(1.5));
+ cipEqu(Double.valueOf(-0.125), new OverloadedNumberUtil.DoubleOrFloat(-0.125));
+ cipEqu(Double.valueOf(Float.MAX_VALUE), new OverloadedNumberUtil.DoubleOrFloat((double) Float.MAX_VALUE));
+ cipEqu(Double.valueOf(-Float.MAX_VALUE), new OverloadedNumberUtil.DoubleOrFloat((double) -Float.MAX_VALUE));
+ cipEqu(Double.valueOf(Float.MAX_VALUE * 10.0));
+ cipEqu(Double.valueOf(-Float.MAX_VALUE * 10.0));
+
+ cipEqu(Double.valueOf(0), new OverloadedNumberUtil.DoubleOrByte(0.0, (byte) 0));
+ cipEqu(Double.valueOf(Byte.MAX_VALUE), new OverloadedNumberUtil.DoubleOrByte((double) Byte.MAX_VALUE, Byte.MAX_VALUE));
+ cipEqu(Double.valueOf(Byte.MIN_VALUE), new OverloadedNumberUtil.DoubleOrByte((double) Byte.MIN_VALUE, Byte.MIN_VALUE));
+
+ cipEqu(Double.valueOf(Byte.MAX_VALUE + 1), new OverloadedNumberUtil.DoubleOrShort((double)
+ (Byte.MAX_VALUE + 1), (short) (Byte.MAX_VALUE + 1)));
+ cipEqu(Double.valueOf(Byte.MIN_VALUE - 1), new OverloadedNumberUtil.DoubleOrShort((double)
+ (Byte.MIN_VALUE - 1), (short) (Byte.MIN_VALUE - 1)));
+
+ cipEqu(Double.valueOf(Short.MAX_VALUE + 1),
+ new OverloadedNumberUtil.DoubleOrIntegerOrFloat((double) Short.MAX_VALUE + 1, Short.MAX_VALUE + 1));
+ cipEqu(Double.valueOf(Short.MIN_VALUE - 1),
+ new OverloadedNumberUtil.DoubleOrIntegerOrFloat((double) Short.MIN_VALUE - 1, Short.MIN_VALUE - 1));
+ cipEqu(Double.valueOf(16777216), new OverloadedNumberUtil.DoubleOrIntegerOrFloat(16777216.0, 16777216));
+ cipEqu(Double.valueOf(-16777216), new OverloadedNumberUtil.DoubleOrIntegerOrFloat(-16777216.0, -16777216));
+
+ cipEqu(Double.valueOf(Integer.MAX_VALUE),
+ new OverloadedNumberUtil.DoubleOrInteger((double) Integer.MAX_VALUE, Integer.MAX_VALUE));
+ cipEqu(Double.valueOf(Integer.MIN_VALUE),
+ new OverloadedNumberUtil.DoubleOrInteger((double) Integer.MIN_VALUE, Integer.MIN_VALUE));
+
+ cipEqu(Double.valueOf(Integer.MAX_VALUE + 1L),
+ new OverloadedNumberUtil.DoubleOrLong(Double.valueOf(Integer.MAX_VALUE + 1L), Integer.MAX_VALUE + 1L));
+ cipEqu(Double.valueOf(Integer.MIN_VALUE - 1L),
+ new OverloadedNumberUtil.DoubleOrLong(Double.valueOf(Integer.MIN_VALUE - 1L), Integer.MIN_VALUE - 1L));
+ cipEqu(Double.valueOf(Long.MAX_VALUE),
+ new OverloadedNumberUtil.DoubleOrFloat((double) Long.MAX_VALUE));
+ cipEqu(Double.valueOf(Long.MIN_VALUE),
+ new OverloadedNumberUtil.DoubleOrFloat((double) Long.MIN_VALUE));
+
+ // When only certain target types are present:
+ cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrByte(5.0, (byte) 5), TypeFlags.BYTE);
+ cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrByte(5.0, (byte) 5), TypeFlags.BYTE | TypeFlags.SHORT);
+ cipEqu(Double.valueOf(-129), TypeFlags.BYTE);
+ cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrShort(5.0, (short) 5), TypeFlags.SHORT);
+ cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrInteger(5.0, 5), TypeFlags.INTEGER);
+ cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrLong(5.0, 5), TypeFlags.LONG);
+ cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrFloat(5.0), TypeFlags.FLOAT);
+ cipEqu(Double.valueOf(5), Double.valueOf(5), TypeFlags.DOUBLE);
+ cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrFloat(5.0),
+ TypeFlags.DOUBLE | TypeFlags.FLOAT);
+ cipEqu(Double.valueOf(5.9), new OverloadedNumberUtil.DoubleOrFloat(5.9),
+ TypeFlags.DOUBLE | TypeFlags.FLOAT);
+ cipEqu(Double.valueOf(5.9),
+ TypeFlags.DOUBLE | TypeFlags.INTEGER);
+ cipEqu(Double.valueOf(5.9), new OverloadedNumberUtil.DoubleOrFloat(5.9),
+ TypeFlags.FLOAT | TypeFlags.INTEGER);
+ cipEqu(Double.valueOf(5.9), TypeFlags.INTEGER);
+ cipEqu(Double.valueOf(Long.MAX_VALUE),
+ new OverloadedNumberUtil.DoubleOrFloat((double) Long.MAX_VALUE),
+ TypeFlags.DOUBLE | TypeFlags.FLOAT);
+ cipEqu(Double.valueOf(Long.MAX_VALUE),
+ TypeFlags.DOUBLE | TypeFlags.LONG);
+ cipEqu(Double.valueOf(Long.MIN_VALUE),
+ new OverloadedNumberUtil.DoubleOrFloat((double) Long.MIN_VALUE),
+ TypeFlags.DOUBLE | TypeFlags.FLOAT);
+ cipEqu(Double.valueOf(Float.MAX_VALUE * 10),
+ TypeFlags.DOUBLE | TypeFlags.FLOAT);
+ cipEqu(Double.valueOf(-Float.MAX_VALUE * 10),
+ TypeFlags.DOUBLE | TypeFlags.FLOAT);
+
+ // Rounded values:
+ cipEqu(Double.valueOf(0.0000009),
+ new OverloadedNumberUtil.DoubleOrByte(0.0000009, (byte) 0));
+ cipEqu(Double.valueOf(-0.0000009),
+ new OverloadedNumberUtil.DoubleOrByte(-0.0000009, (byte) 0));
+ cipEqu(Double.valueOf(0.9999991),
+ new OverloadedNumberUtil.DoubleOrByte(0.9999991, (byte) 1));
+ cipEqu(Double.valueOf(-0.9999991),
+ new OverloadedNumberUtil.DoubleOrByte(-0.9999991, (byte) -1));
+ cipEqu(Double.valueOf(0.0000009),
+ new OverloadedNumberUtil.DoubleOrShort(0.0000009, (short) 0),
+ TypeFlags.SHORT | TypeFlags.DOUBLE);
+ cipEqu(Double.valueOf(0.0000009), new OverloadedNumberUtil.DoubleOrInteger(0.0000009, 0),
+ TypeFlags.INTEGER | TypeFlags.DOUBLE);
+ cipEqu(Double.valueOf(0.0000009), new OverloadedNumberUtil.DoubleOrLong(0.0000009, 0),
+ TypeFlags.LONG | TypeFlags.DOUBLE);
+ cipEqu(Double.valueOf(0.0000009),
+ new OverloadedNumberUtil.DoubleOrByte(0.0000009, (byte) 0), TypeFlags.BYTE);
+ cipEqu(Double.valueOf(0.0000009),
+ new OverloadedNumberUtil.DoubleOrShort(0.0000009, (short) 0), TypeFlags.SHORT);
+ cipEqu(Double.valueOf(0.0000009),
+ new OverloadedNumberUtil.DoubleOrInteger(0.0000009, 0), TypeFlags.INTEGER);
+ cipEqu(Double.valueOf(0.0000009),
+ new OverloadedNumberUtil.DoubleOrLong(0.0000009, 0L), TypeFlags.LONG);
+ cipEqu(Double.valueOf(0.9999999),
+ new OverloadedNumberUtil.DoubleOrInteger(0.9999999, 1), TypeFlags.INTEGER);
+ cipEqu(Double.valueOf(Byte.MAX_VALUE + 0.9e-6),
+ new OverloadedNumberUtil.DoubleOrByte(Byte.MAX_VALUE + 0.9e-6, Byte.MAX_VALUE));
+ cipEqu(Double.valueOf(Byte.MIN_VALUE - 0.9e-6),
+ new OverloadedNumberUtil.DoubleOrByte(Byte.MIN_VALUE - 0.9e-6, Byte.MIN_VALUE));
+ cipEqu(Double.valueOf(Byte.MAX_VALUE + 1.1e-6),
+ new OverloadedNumberUtil.DoubleOrFloat(Byte.MAX_VALUE + 1.1e-6));
+ cipEqu(Double.valueOf(Byte.MIN_VALUE - 1.1e-6),
+ new OverloadedNumberUtil.DoubleOrFloat(Byte.MIN_VALUE - 1.1e-6));
+ cipEqu(Double.valueOf(Byte.MAX_VALUE + 0.9999991),
+ new OverloadedNumberUtil.DoubleOrShort(
+ Byte.MAX_VALUE + 0.9999991, (short) (Byte.MAX_VALUE + 1)));
+ cipEqu(Double.valueOf(Byte.MIN_VALUE - 0.9999991),
+ new OverloadedNumberUtil.DoubleOrShort(
+ Byte.MIN_VALUE - 0.9999991, (short) (Byte.MIN_VALUE - 1)));
+
+ cipEqu(Double.valueOf(Byte.MAX_VALUE + 1), new OverloadedNumberUtil.DoubleOrShort((double)
+ (Byte.MAX_VALUE + 1), (short) (Byte.MAX_VALUE + 1)));
+ cipEqu(Double.valueOf(Byte.MIN_VALUE - 1), new OverloadedNumberUtil.DoubleOrShort((double)
+ (Byte.MIN_VALUE - 1), (short) (Byte.MIN_VALUE - 1)));
+
+ cipEqu(Short.MAX_VALUE + 0.9999991,
+ new OverloadedNumberUtil.DoubleOrIntegerOrFloat(Short.MAX_VALUE + 0.9999991, Short.MAX_VALUE + 1));
+ cipEqu(Short.MIN_VALUE - 0.9999991,
+ new OverloadedNumberUtil.DoubleOrIntegerOrFloat(Short.MIN_VALUE - 0.9999991, Short.MIN_VALUE - 1));
+ cipEqu(16777216 + 0.9e-6,
+ new OverloadedNumberUtil.DoubleOrIntegerOrFloat(16777216 + 0.9e-6, 16777216));
+ cipEqu(-16777216 - 0.9e-6,
+ new OverloadedNumberUtil.DoubleOrIntegerOrFloat(-16777216 - 0.9e-6, -16777216));
+
+ cipEqu(Integer.MAX_VALUE + 0.9e-6,
+ new OverloadedNumberUtil.DoubleOrInteger(Integer.MAX_VALUE + 0.9e-6, Integer.MAX_VALUE));
+ cipEqu(Integer.MIN_VALUE - 0.9e-6,
+ new OverloadedNumberUtil.DoubleOrInteger(Integer.MIN_VALUE - 0.9e-6, Integer.MIN_VALUE));
+
+ cipEqu(Integer.MAX_VALUE + 1L + 0.9e-6,
+ new OverloadedNumberUtil.DoubleOrFloat(Integer.MAX_VALUE + 1L + 0.9e-6));
+ cipEqu(Integer.MIN_VALUE - 1L - 0.9e-6,
+ new OverloadedNumberUtil.DoubleOrFloat(Integer.MIN_VALUE - 1L - 0.9e-6));
+ cipEqu(Long.MAX_VALUE + 0.9e-6,
+ new OverloadedNumberUtil.DoubleOrFloat(Long.MAX_VALUE + 0.9e-6));
+ cipEqu(Long.MIN_VALUE - 0.9e-6,
+ new OverloadedNumberUtil.DoubleOrFloat(Long.MIN_VALUE - 0.9e-6));
+ }
+
+ @SuppressWarnings("boxing")
+ public void testFloatCoercion() {
+ cipEqu(1.00002f);
+ cipEqu(-1.00002f);
+ cipEqu(1.999989f);
+ cipEqu(-1.999989f);
+ cipEqu(16777218f);
+ cipEqu(-16777218f);
+
+ cipEqu(1f, new OverloadedNumberUtil.FloatOrByte(1f, (byte) 1));
+ cipEqu(-1f, new OverloadedNumberUtil.FloatOrByte(-1f, (byte) -1));
+ cipEqu(1.000009f, new OverloadedNumberUtil.FloatOrByte(1.000009f, (byte) 1));
+ cipEqu(-1.000009f, new OverloadedNumberUtil.FloatOrByte(-1.000009f, (byte) -1));
+ cipEqu(1.999991f, new OverloadedNumberUtil.FloatOrByte(1.999991f, (byte) 2));
+ cipEqu(-1.999991f, new OverloadedNumberUtil.FloatOrByte(-1.999991f, (byte) -2));
+
+ cipEqu(1000f, new OverloadedNumberUtil.FloatOrShort(1000f, (short) 1000));
+ cipEqu(-1000f, new OverloadedNumberUtil.FloatOrShort(-1000f, (short) -1000));
+ cipEqu(1000.00006f);
+
+ cipEqu(60000f, new OverloadedNumberUtil.FloatOrInteger(60000f, 60000));
+ cipEqu(-60000f, new OverloadedNumberUtil.FloatOrInteger(-60000f, -60000));
+ cipEqu(60000.004f);
+
+ cipEqu(100f, new OverloadedNumberUtil.FloatOrByte(100f, (byte) 100), TypeFlags.MASK_KNOWN_INTEGERS);
+ cipEqu(1000f, new OverloadedNumberUtil.FloatOrShort(1000f, (short) 1000), TypeFlags.MASK_KNOWN_INTEGERS);
+ cipEqu(60000f, new OverloadedNumberUtil.FloatOrInteger(60000f, 60000), TypeFlags.MASK_KNOWN_INTEGERS);
+ cipEqu(60000f, new OverloadedNumberUtil.FloatOrInteger(60000f, 60000), TypeFlags.LONG);
+ cipEqu((float) Integer.MAX_VALUE, (float) Integer.MAX_VALUE, TypeFlags.LONG);
+ cipEqu((float) -Integer.MAX_VALUE, (float) -Integer.MAX_VALUE);
+
+ cipEqu(0.5f, 0.5f, TypeFlags.DOUBLE | TypeFlags.FLOAT);
+ cipEqu(0.5f, 0.5f, TypeFlags.DOUBLE);
+ }
+
+ public void testBigIntegerCoercion() {
+ BigInteger bi;
+
+ cipEqu(BigInteger.ZERO, new OverloadedNumberUtil.BigIntegerOrByte(BigInteger.ZERO));
+ bi = new BigInteger(String.valueOf(Byte.MAX_VALUE));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrByte(bi));
+ bi = new BigInteger(String.valueOf(Byte.MIN_VALUE));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrByte(bi));
+
+ bi = new BigInteger(String.valueOf(Byte.MAX_VALUE + 1));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi));
+ bi = new BigInteger(String.valueOf(Byte.MIN_VALUE - 1));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi));
+ bi = new BigInteger(String.valueOf(Short.MAX_VALUE));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi));
+ bi = new BigInteger(String.valueOf(Short.MIN_VALUE));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi));
+
+ bi = new BigInteger(String.valueOf(Short.MAX_VALUE + 1));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi));
+ bi = new BigInteger(String.valueOf(Short.MIN_VALUE - 1));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi));
+ bi = new BigInteger(String.valueOf(Integer.MAX_VALUE));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi));
+ bi = new BigInteger(String.valueOf(Integer.MIN_VALUE));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi));
+
+ bi = new BigInteger(String.valueOf(Integer.MAX_VALUE + 1L));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi));
+ bi = new BigInteger(String.valueOf(Integer.MIN_VALUE - 1L));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi));
+ bi = new BigInteger(String.valueOf(Long.MAX_VALUE));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi));
+ bi = new BigInteger(String.valueOf(Long.MIN_VALUE));
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi));
+
+ cipEqu(new BigInteger(String.valueOf(Long.MAX_VALUE)).add(BigInteger.ONE));
+ cipEqu(new BigInteger(String.valueOf(Long.MIN_VALUE)).subtract(BigInteger.ONE));
+
+ bi = new BigInteger("0");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi),
+ TypeFlags.DOUBLE | TypeFlags.FLOAT);
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi),
+ TypeFlags.MASK_KNOWN_NONINTEGERS);
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi),
+ TypeFlags.FLOAT);
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi),
+ TypeFlags.DOUBLE);
+
+ bi = new BigInteger("16777215");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+ bi = new BigInteger("-16777215");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+
+ bi = new BigInteger("16777216");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+ bi = new BigInteger("-16777216");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+
+ bi = new BigInteger("16777217");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+ cipEqu(bi, TypeFlags.FLOAT);
+ bi = new BigInteger("-16777217");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+ cipEqu(bi, TypeFlags.FLOAT);
+
+ bi = new BigInteger("9007199254740991");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE);
+ bi = new BigInteger("-9007199254740991");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE);
+
+ bi = new BigInteger("9007199254740992");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE);
+ bi = new BigInteger("-9007199254740992");
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
+ cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE);
+
+ bi = new BigInteger("9007199254740993");
+ cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS);
+ cipEqu(bi, TypeFlags.DOUBLE | TypeFlags.FLOAT);
+ cipEqu(bi, TypeFlags.FLOAT);
+ cipEqu(bi, TypeFlags.DOUBLE);
+ bi = new BigInteger("-9007199254740993");
+ cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS);
+ cipEqu(bi, TypeFlags.DOUBLE | TypeFlags.FLOAT);
+ cipEqu(bi, TypeFlags.FLOAT);
+ cipEqu(bi, TypeFlags.DOUBLE);
+
+ bi = new BigInteger("9007199254740994");
+ cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS);
+ bi = new BigInteger("-9007199254740994");
+ cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS);
+ }
+
+ private void cipEqu(Number actualAndExpected) {
+ cipEqu(actualAndExpected, actualAndExpected, -1);
+ }
+
+ private void cipEqu(Number actual, Number expected) {
+ cipEqu(actual, expected, -1);
+ }
+
+ private void cipEqu(Number actualAndExpected, int flags) {
+ cipEqu(actualAndExpected, actualAndExpected, flags);
+ }
+
+ @SuppressWarnings("boxing")
+ private void cipEqu(Number actual, Number expected, int flags) {
+ Number res = OverloadedNumberUtil.addFallbackType(actual, flags);
+ assertEquals(expected.getClass(), res.getClass());
+ assertEquals(expected, res);
+
+ // Some number types wrap the number with multiple types and equals() only compares one of them. So we try to
+ // catch any inconsistency:
+ assertEquals(expected.byteValue(), res.byteValue());
+ assertEquals(expected.shortValue(), res.shortValue());
+ assertEquals(expected.intValue(), res.intValue());
+ assertEquals(expected.longValue(), res.longValue());
+ assertEquals(expected.floatValue(), res.floatValue());
+ assertEquals(expected.doubleValue(), res.doubleValue());
+ }
+
+ public void testGetArgumentConversionPrice() {
+ // Unknown number types:
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ RationalNumber.class, Integer.class));
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, RationalNumber.class));
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ RationalNumber.class, Float.class));
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ Float.class, RationalNumber.class));
+ assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
+ RationalNumber.class, RationalNumber.class));
+
+ // Fully check some rows (not all of them; the code is generated anyways):
+
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, Byte.class));
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, Short.class));
+ assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, Integer.class));
+ assertEquals(10004, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, Long.class));
+ assertEquals(10005, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, BigInteger.class));
+ assertEquals(30006, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, Float.class));
+ assertEquals(20007, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, Double.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, BigDecimal.class));
+
+ assertEquals(45001, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigDecimal.class, Byte.class));
+ assertEquals(44002, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigDecimal.class, Short.class));
+ assertEquals(41003, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigDecimal.class, Integer.class));
+ assertEquals(41004, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigDecimal.class, Long.class));
+ assertEquals(40005, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigDecimal.class, BigInteger.class));
+ assertEquals(33006, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigDecimal.class, Float.class));
+ assertEquals(32007, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigDecimal.class, Double.class));
+ assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigDecimal.class, BigDecimal.class));
+
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrLong.class, Byte.class));
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrLong.class, Short.class));
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrLong.class, Integer.class));
+ assertEquals(21004, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrLong.class, Long.class));
+ assertEquals(21005, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrLong.class, BigInteger.class));
+ assertEquals(40006, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrLong.class, Float.class));
+ assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrLong.class, Double.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrLong.class, BigDecimal.class));
+
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrDouble.class, Byte.class));
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrDouble.class, Short.class));
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrDouble.class, Integer.class));
+ assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrDouble.class, Long.class));
+ assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrDouble.class, BigInteger.class));
+ assertEquals(40006, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrDouble.class, Float.class));
+ assertEquals(20007, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrDouble.class, Double.class));
+ assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrDouble.class, BigDecimal.class));
+
+ // Check if all fromC is present:
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ Byte.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ Short.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ Integer.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ Long.class, BigDecimal.class));
+ assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigInteger.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ Float.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ Double.class, BigDecimal.class));
+ assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
+ BigDecimal.class, BigDecimal.class));
+ assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.IntegerBigDecimal.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrFloat.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.FloatOrByte.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrShort.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.FloatOrByte.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.FloatOrShort.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.FloatOrInteger.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrByte.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrIntegerOrFloat.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrInteger.class, BigDecimal.class));
+ assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.DoubleOrLong.class, BigDecimal.class));
+ assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrByte.class, BigDecimal.class));
+ assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrShort.class, BigDecimal.class));
+ assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrInteger.class, BigDecimal.class));
+ assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrLong.class, BigDecimal.class));
+ assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrFloat.class, BigDecimal.class));
+ assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
+ OverloadedNumberUtil.BigIntegerOrDouble.class, BigDecimal.class));
+ }
+
+}
[20/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
Removed BeansWrapper, merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans packageis gone now). It works, but there's a lot of unused classes and logic now, which will have to be removed.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/051a0822
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/051a0822
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/051a0822
Branch: refs/heads/3
Commit: 051a0822329aae44642b491fb1a10e75ad4ea778
Parents: 3187a09
Author: ddekany <dd...@apache.org>
Authored: Mon Feb 27 10:36:49 2017 +0100
Committer: ddekany <dd...@apache.org>
Committed: Tue Feb 28 23:57:22 2017 +0100
----------------------------------------------------------------------
.../Java-code-style-FreeMarker.xml | 20 +-
.../apache/freemarker/core/ASTExpression.java | 2 +-
.../core/BuiltInsForMultipleTypes.java | 4 +-
.../freemarker/core/BuiltInsForSequences.java | 2 +-
.../freemarker/core/BuiltInsForStringsMisc.java | 14 +-
.../apache/freemarker/core/Configurable.java | 14 +-
.../org/apache/freemarker/core/_CoreLogs.java | 12 +-
.../core/_ErrorDescriptionBuilder.java | 2 +-
.../core/_ObjectBuilderSettingEvaluator.java | 6 +-
.../core/_SettingEvaluationEnvironment.java | 8 +-
.../freemarker/core/model/ObjectWrapper.java | 8 +-
.../core/model/TemplateMethodModel.java | 2 +-
.../core/model/TemplateMethodModelEx.java | 2 +-
.../freemarker/core/model/impl/APIModel.java | 45 +
.../core/model/impl/ArgumentTypes.java | 647 +++++++
.../freemarker/core/model/impl/ArrayModel.java | 126 ++
.../freemarker/core/model/impl/BeanModel.java | 362 ++++
.../core/model/impl/BeansModelCache.java | 73 +
.../core/model/impl/BooleanModel.java | 40 +
.../model/impl/CallableMemberDescriptor.java | 56 +
.../core/model/impl/CharacterOrString.java | 45 +
.../core/model/impl/ClassBasedModelFactory.java | 148 ++
.../core/model/impl/ClassChangeNotifier.java | 32 +
.../core/model/impl/ClassIntrospector.java | 807 +++++++++
.../model/impl/ClassIntrospectorBuilder.java | 190 ++
.../core/model/impl/CollectionAdapter.java | 88 +
.../core/model/impl/CollectionModel.java | 109 ++
.../freemarker/core/model/impl/DateModel.java | 76 +
.../model/impl/DefaultEnumerationAdapter.java | 121 ++
.../core/model/impl/DefaultObjectWrapper.java | 1474 +++++++++++++++-
.../model/impl/DefaultObjectWrapperBuilder.java | 100 +-
.../impl/DefaultObjectWrapperConfiguration.java | 187 +-
.../impl/EmptyCallableMemberDescriptor.java | 35 +
.../model/impl/EmptyMemberAndArguments.java | 93 +
.../core/model/impl/EnumerationModel.java | 108 ++
.../freemarker/core/model/impl/HashAdapter.java | 180 ++
.../model/impl/InvalidPropertyException.java | 34 +
.../core/model/impl/IteratorModel.java | 112 ++
.../model/impl/JRebelClassChangeNotifier.java | 58 +
.../freemarker/core/model/impl/MapModel.java | 120 ++
.../MaybeEmptyCallableMemberDescriptor.java | 25 +
.../impl/MaybeEmptyMemberAndArguments.java | 22 +
.../core/model/impl/MemberAndArguments.java | 64 +
.../model/impl/MethodAppearanceFineTuner.java | 156 ++
.../core/model/impl/MethodSorter.java | 32 +
.../freemarker/core/model/impl/ModelCache.java | 143 ++
.../core/model/impl/ModelFactory.java | 34 +
.../NonPrimitiveArrayBackedReadOnlyList.java | 42 +
.../freemarker/core/model/impl/NumberModel.java | 60 +
.../model/impl/OverloadedFixArgsMethods.java | 99 ++
.../core/model/impl/OverloadedMethods.java | 271 +++
.../core/model/impl/OverloadedMethodsModel.java | 82 +
.../model/impl/OverloadedMethodsSubset.java | 402 +++++
.../core/model/impl/OverloadedNumberUtil.java | 1289 ++++++++++++++
.../model/impl/OverloadedVarArgsMethods.java | 245 +++
.../impl/PrimtiveArrayBackedReadOnlyList.java | 47 +
.../ReflectionCallableMemberDescriptor.java | 95 +
.../core/model/impl/ResourceBundleModel.java | 190 ++
.../core/model/impl/SequenceAdapter.java | 68 +
.../freemarker/core/model/impl/SetAdapter.java | 32 +
.../freemarker/core/model/impl/SimpleHash.java | 5 +-
.../core/model/impl/SimpleMapModel.java | 129 ++
.../core/model/impl/SimpleMethod.java | 174 ++
.../core/model/impl/SimpleMethodModel.java | 133 ++
.../core/model/impl/SimpleObjectWrapper.java | 19 +-
.../core/model/impl/SimpleSequence.java | 5 +-
.../core/model/impl/SingletonCustomizer.java | 51 +
.../freemarker/core/model/impl/StaticModel.java | 177 ++
.../core/model/impl/StaticModels.java | 43 +
.../freemarker/core/model/impl/StringModel.java | 63 +
.../freemarker/core/model/impl/TypeFlags.java | 130 ++
.../core/model/impl/UnsafeMethods.java | 112 ++
.../freemarker/core/model/impl/_EnumModels.java | 54 +
.../freemarker/core/model/impl/_MethodUtil.java | 293 ++++
.../freemarker/core/model/impl/_ModelAPI.java | 220 +++
.../core/model/impl/_ModelImplApi.java | 37 -
.../core/model/impl/_StaticObjectWrappers.java | 5 -
.../core/model/impl/beans/APIModel.java | 45 -
.../core/model/impl/beans/ArgumentTypes.java | 647 -------
.../core/model/impl/beans/ArrayModel.java | 126 --
.../core/model/impl/beans/BeanModel.java | 365 ----
.../core/model/impl/beans/BeansModelCache.java | 73 -
.../core/model/impl/beans/BeansWrapper.java | 1644 ------------------
.../model/impl/beans/BeansWrapperBuilder.java | 159 --
.../impl/beans/BeansWrapperConfiguration.java | 232 ---
.../core/model/impl/beans/BooleanModel.java | 40 -
.../impl/beans/CallableMemberDescriptor.java | 56 -
.../model/impl/beans/CharacterOrString.java | 45 -
.../impl/beans/ClassBasedModelFactory.java | 148 --
.../model/impl/beans/ClassChangeNotifier.java | 32 -
.../model/impl/beans/ClassIntrospector.java | 808 ---------
.../impl/beans/ClassIntrospectorBuilder.java | 190 --
.../model/impl/beans/CollectionAdapter.java | 88 -
.../core/model/impl/beans/CollectionModel.java | 109 --
.../core/model/impl/beans/DateModel.java | 79 -
.../beans/EmptyCallableMemberDescriptor.java | 35 -
.../impl/beans/EmptyMemberAndArguments.java | 93 -
.../core/model/impl/beans/EnumerationModel.java | 108 --
.../core/model/impl/beans/HashAdapter.java | 180 --
.../impl/beans/InvalidPropertyException.java | 34 -
.../core/model/impl/beans/IteratorModel.java | 112 --
.../impl/beans/JRebelClassChangeNotifier.java | 58 -
.../core/model/impl/beans/MapModel.java | 120 --
.../MaybeEmptyCallableMemberDescriptor.java | 25 -
.../beans/MaybeEmptyMemberAndArguments.java | 22 -
.../model/impl/beans/MemberAndArguments.java | 64 -
.../impl/beans/MethodAppearanceFineTuner.java | 94 -
.../core/model/impl/beans/MethodSorter.java | 32 -
.../core/model/impl/beans/ModelCache.java | 143 --
.../core/model/impl/beans/ModelFactory.java | 34 -
.../NonPrimitiveArrayBackedReadOnlyList.java | 42 -
.../core/model/impl/beans/NumberModel.java | 60 -
.../impl/beans/OverloadedFixArgsMethods.java | 99 --
.../model/impl/beans/OverloadedMethods.java | 271 ---
.../impl/beans/OverloadedMethodsModel.java | 83 -
.../impl/beans/OverloadedMethodsSubset.java | 402 -----
.../model/impl/beans/OverloadedNumberUtil.java | 1289 --------------
.../impl/beans/OverloadedVarArgsMethods.java | 245 ---
.../beans/PrimtiveArrayBackedReadOnlyList.java | 47 -
.../ReflectionCallableMemberDescriptor.java | 95 -
.../model/impl/beans/ResourceBundleModel.java | 190 --
.../core/model/impl/beans/SequenceAdapter.java | 68 -
.../core/model/impl/beans/SetAdapter.java | 32 -
.../core/model/impl/beans/SimpleMapModel.java | 132 --
.../core/model/impl/beans/SimpleMethod.java | 174 --
.../model/impl/beans/SimpleMethodModel.java | 134 --
.../model/impl/beans/SingletonCustomizer.java | 51 -
.../core/model/impl/beans/StaticModel.java | 177 --
.../core/model/impl/beans/StaticModels.java | 43 -
.../core/model/impl/beans/StringModel.java | 63 -
.../core/model/impl/beans/TypeFlags.java | 130 --
.../core/model/impl/beans/UnsafeMethods.java | 112 --
.../core/model/impl/beans/_BeansAPI.java | 220 ---
.../core/model/impl/beans/_EnumModels.java | 54 -
.../core/model/impl/beans/_MethodUtil.java | 293 ----
.../core/model/impl/beans/package.html | 39 -
.../freemarker/core/model/impl/package.html | 39 +
.../apache/freemarker/core/util/FTLUtil.java | 22 +-
.../apache/freemarker/core/util/_ClassUtil.java | 2 +-
.../freemarker/servlet/FreemarkerServlet.java | 4 -
.../jsp/CustomTagAndELFunctionCombiner.java | 2 +-
.../freemarker/servlet/jsp/JspContextModel.java | 2 +-
.../freemarker/servlet/jsp/JspTagModelBase.java | 6 +-
.../freemarker/servlet/jsp/TaglibFactory.java | 17 +-
.../model/impl/beans/unsafeMethods.properties | 98 --
.../core/model/impl/unsafeMethods.properties | 98 ++
src/manual/en_US/FM3-CHANGE-LOG.txt | 6 +-
.../freemarker/core/ConfigurationTest.java | 7 +-
.../freemarker/core/IteratorIssuesTest.java | 43 +-
.../core/ObjectBuilderSettingsTest.java | 40 +-
.../impl/AbstractParallelIntrospectionTest.java | 125 ++
.../model/impl/AlphabeticalMethodSorter.java | 49 +
.../CommonSupertypeForUnwrappingHintTest.java | 129 ++
.../model/impl/DefaultObjectWrapperDesc.java | 30 +
.../model/impl/DefaultObjectWrapperInc.java | 30 +
...jectWrapperModelFactoryRegistrationTest.java | 94 +
.../impl/DefaultObjectWrapperReadOnlyTest.java | 87 +
.../DefaultObjectWrapperSingletonsTest.java | 689 ++++++++
.../model/impl/DefaultObjectWrapperTest.java | 191 +-
.../DefaultObjectWrapperWithShortedMethods.java | 41 +
.../DefaultObjectWrapperWithSortedMethods.java | 40 +
.../core/model/impl/EnumModelsTest.java | 84 +
.../core/model/impl/ErrorMessagesTest.java | 170 ++
.../impl/FineTuneMethodAppearanceTest.java | 64 +
.../GetlessMethodsAsPropertyGettersRule.java | 67 +
.../core/model/impl/IsApplicableTest.java | 171 ++
.../impl/IsMoreSpecificParameterTypeTest.java | 98 ++
.../Java7MembersOnlyDefaultObjectWrapper.java | 102 ++
.../impl/ManyObjectsOfDifferentClasses.java | 249 +++
.../impl/ManyStaticsOfDifferentClasses.java | 236 +++
.../model/impl/MiscNumericalOperationsTest.java | 111 ++
.../model/impl/ModelAPINewInstanceTest.java | 134 ++
.../core/model/impl/ModelCacheTest.java | 69 +
.../model/impl/OverloadedNumberUtilTest.java | 585 +++++++
.../impl/ParameterListPreferabilityTest.java | 445 +++++
.../impl/PrallelObjectIntrospectionTest.java | 43 +
.../impl/PrallelStaticIntrospectionTest.java | 47 +
.../core/model/impl/RationalNumber.java | 90 +
.../core/model/impl/StaticModelsTest.java | 90 +
.../core/model/impl/TypeFlagsTest.java | 671 +++++++
.../AbstractParallelIntrospectionTest.java | 125 --
.../impl/beans/AlphabeticalMethodSorter.java | 49 -
.../impl/beans/BeansAPINewInstanceTest.java | 134 --
.../model/impl/beans/BeansWrapperBasics.java | 62 -
.../impl/beans/BeansWrapperCachesTest.java | 99 --
.../core/model/impl/beans/BeansWrapperDesc.java | 30 -
.../core/model/impl/beans/BeansWrapperInc.java | 30 -
.../model/impl/beans/BeansWrapperMiscTest.java | 57 -
.../impl/beans/BeansWrapperReadOnlyTest.java | 95 -
.../impl/beans/BeansWrapperSingletonsTest.java | 743 --------
.../beans/BeansWrapperWithShortedMethods.java | 41 -
.../CommonSupertypeForUnwrappingHintTest.java | 129 --
.../impl/beans/DefaultObjectWrapperDesc.java | 30 -
.../impl/beans/DefaultObjectWrapperInc.java | 30 -
.../DefaultObjectWrapperWithSortedMethods.java | 41 -
.../core/model/impl/beans/EnumModelsTest.java | 84 -
.../model/impl/beans/ErrorMessagesTest.java | 170 --
.../beans/FineTuneMethodAppearanceTest.java | 65 -
.../GetlessMethodsAsPropertyGettersRule.java | 70 -
.../core/model/impl/beans/IsApplicableTest.java | 171 --
.../beans/IsMoreSpecificParameterTypeTest.java | 98 --
.../beans/Java7MembersOnlyBeansWrapper.java | 102 --
.../beans/ManyObjectsOfDifferentClasses.java | 249 ---
.../beans/ManyStaticsOfDifferentClasses.java | 236 ---
.../impl/beans/MiscNumericalOperationsTest.java | 111 --
.../core/model/impl/beans/ModelCacheTest.java | 65 -
.../impl/beans/OverloadedNumberUtilTest.java | 585 -------
.../beans/ParameterListPreferabilityTest.java | 445 -----
.../beans/PrallelObjectIntrospectionTest.java | 43 -
.../beans/PrallelStaticIntrospectionTest.java | 47 -
.../core/model/impl/beans/RationalNumber.java | 90 -
.../core/model/impl/beans/StaticModelsTest.java | 90 -
.../core/model/impl/beans/TypeFlagsTest.java | 671 -------
.../servlet/jsp/RealServletContainertTest.java | 19 +-
.../freemarker/servlet/jsp/TLDParsingTest.java | 8 +-
.../test/templatesuite/TemplateTestCase.java | 63 +-
.../test/templatesuite/models/Listables.java | 5 -
.../models/OverloadedMethods2.java | 2 +-
.../SimpleMapAndCollectionObjectWrapper.java | 3 +-
.../expected/attributes-modernModels.txt | 4 +-
.../basic/WEB-INF/expected/attributes.txt | 4 +-
.../test/templatesuite/expected/bean-maps.txt | 115 --
.../test/templatesuite/expected/beans.txt | 56 -
.../expected/default-object-wrapper.txt | 56 +
.../test/templatesuite/expected/listhash.txt | 44 -
.../templatesuite/templates/api-builtins.ftl | 8 +-
.../test/templatesuite/templates/bean-maps.ftl | 58 -
.../test/templatesuite/templates/beans.ftl | 60 -
.../templates/default-object-wrapper.ftl | 60 +
.../templates/overloaded-methods.ftl | 4 +-
.../freemarker/test/templatesuite/testcases.xml | 53 +-
231 files changed, 15741 insertions(+), 16580 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml
----------------------------------------------------------------------
diff --git a/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml b/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml
index 9615559..ae87ca3 100644
--- a/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml
+++ b/src/ide-settings/IntelliJ-IDEA/Java-code-style-FreeMarker.xml
@@ -1,3 +1,21 @@
+<!--
+ 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.
+-->
<code_scheme name="FreeMarker">
<option name="LINE_SEPARATOR" value="
" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
@@ -27,7 +45,7 @@
<option name="JD_PARAM_DESCRIPTION_ON_NEW_LINE" value="true" />
<option name="WRAP_COMMENTS" value="true" />
<JavaCodeStyleSettings>
- <option name="CLASS_NAMES_IN_JAVADOC" value="2" />
+ <option name="CLASS_NAMES_IN_JAVADOC" value="3" />
</JavaCodeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="120" />
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/ASTExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTExpression.java b/src/main/java/org/apache/freemarker/core/ASTExpression.java
index 6635e35..af08ccd 100644
--- a/src/main/java/org/apache/freemarker/core/ASTExpression.java
+++ b/src/main/java/org/apache/freemarker/core/ASTExpression.java
@@ -29,7 +29,7 @@ import org.apache.freemarker.core.model.TemplateModelException;
import org.apache.freemarker.core.model.TemplateNumberModel;
import org.apache.freemarker.core.model.TemplateScalarModel;
import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.beans.BeanModel;
+import org.apache.freemarker.core.model.impl.BeanModel;
/**
* AST expression node superclass
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java b/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
index b7bd7f6..d7f0249 100644
--- a/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
+++ b/src/main/java/org/apache/freemarker/core/BuiltInsForMultipleTypes.java
@@ -42,8 +42,8 @@ import org.apache.freemarker.core.model.TemplateTransformModel;
import org.apache.freemarker.core.model.impl.SimpleDate;
import org.apache.freemarker.core.model.impl.SimpleNumber;
import org.apache.freemarker.core.model.impl.SimpleScalar;
-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.OverloadedMethodsModel;
+import org.apache.freemarker.core.model.impl.SimpleMethodModel;
import org.apache.freemarker.core.util.BugException;
import org.apache.freemarker.core.valueformat.TemplateDateFormat;
import org.apache.freemarker.core.valueformat.TemplateNumberFormat;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java b/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
index 92c59b8..c54328c 100644
--- a/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
+++ b/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java
@@ -44,7 +44,7 @@ import org.apache.freemarker.core.model.impl.CollectionAndSequence;
import org.apache.freemarker.core.model.impl.SimpleNumber;
import org.apache.freemarker.core.model.impl.SimpleScalar;
import org.apache.freemarker.core.model.impl.TemplateModelListSequence;
-import org.apache.freemarker.core.model.impl.beans.CollectionModel;
+import org.apache.freemarker.core.model.impl.CollectionModel;
import org.apache.freemarker.core.util.BugException;
import org.apache.freemarker.core.util._StringUtil;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
index 12e1207..712ff76 100644
--- a/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
+++ b/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java
@@ -35,8 +35,8 @@ import org.apache.freemarker.core.model.TemplateSequenceModel;
import org.apache.freemarker.core.model.TemplateTransformModel;
import org.apache.freemarker.core.model.impl.SimpleNumber;
import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
-import org.apache.freemarker.core.model.impl.beans.BeanModel;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
+import org.apache.freemarker.core.model.impl.BeanModel;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
class BuiltInsForStringsMisc {
@@ -289,11 +289,11 @@ class BuiltInsForStringsMisc {
@Override
public Object exec(List arguments) throws TemplateModelException {
ObjectWrapper ow = env.getObjectWrapper();
- BeansWrapper bw =
- ow instanceof BeansWrapper
- ? (BeansWrapper) ow
- : _StaticObjectWrappers.BEANS_WRAPPER;
- return bw.newInstance(cl, arguments);
+ DefaultObjectWrapper dow =
+ ow instanceof DefaultObjectWrapper
+ ? (DefaultObjectWrapper) ow
+ : _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER;
+ return dow.newInstance(cl, arguments);
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/Configurable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configurable.java b/src/main/java/org/apache/freemarker/core/Configurable.java
index 78cd125..c1252d5 100644
--- a/src/main/java/org/apache/freemarker/core/Configurable.java
+++ b/src/main/java/org/apache/freemarker/core/Configurable.java
@@ -48,7 +48,6 @@ import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
import org.apache.freemarker.core.model.impl.SimpleObjectWrapper;
import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
import org.apache.freemarker.core.outputformat.OutputFormat;
import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
import org.apache.freemarker.core.outputformat.impl.PlainTextOutputFormat;
@@ -1805,18 +1804,17 @@ public class Configurable {
* <li><p>{@code "object_wrapper"}:
* See {@link #setObjectWrapper(ObjectWrapper)}.
* <br>String value: If the value contains dot, then it's interpreted as an <a href="#fm_obe">object builder
- * expression</a>, with the addition that {@link BeansWrapper}, {@link DefaultObjectWrapper} and
+ * expression</a>, with the addition that {@link DefaultObjectWrapper}, {@link DefaultObjectWrapper} and
* {@link SimpleObjectWrapper} can be referred without package name. For example, these strings are valid
* values: {@code "DefaultObjectWrapper(3.0.0)"},
- * {@code "BeansWrapper(2.3.21, simpleMapWrapper=true)"}.
+ * {@code "DefaultObjectWrapper(2.3.21, simpleMapWrapper=true)"}.
* <br>If the value does not contain dot, then it must be one of these special values (case insensitive):
* {@code "default"} means the default of {@link Configuration} (the default depends on the
* {@code Configuration#Configuration(Version) incompatible_improvements}, but a bug existed in 2.3.21 where
* that was ignored),
* {@code "default_2_3_0"} (means {@link _StaticObjectWrappers#DEFAULT_OBJECT_WRAPPER})
- * {@code "simple"} (means the deprecated {@link _StaticObjectWrappers#SIMPLE_OBJECT_WRAPPER},
- * {@code "beans"} (means {@link _StaticObjectWrappers#BEANS_WRAPPER})
- *
+ * {@code "simple"} (means the deprecated {@link _StaticObjectWrappers#SIMPLE_OBJECT_WRAPPER}
+ *
* <li><p>{@code "number_format"}: See {@link #setNumberFormat(String)}.
*
* <li><p>{@code "boolean_format"}: See {@link #setBooleanFormat(String)} .
@@ -2116,7 +2114,7 @@ public class Configurable {
* </li>
* <li>
* <p>The following classes can be referred to with simple (unqualified) name instead of fully qualified name:
- * {@link DefaultObjectWrapper}, {@link BeansWrapper}, {@link SimpleObjectWrapper}, {@link Locale},
+ * {@link DefaultObjectWrapper}, {@link DefaultObjectWrapper}, {@link SimpleObjectWrapper}, {@link Locale},
* {@link TemplateConfiguration}, {@link PathGlobMatcher}, {@link FileNameGlobMatcher}, {@link PathRegexMatcher},
* {@link AndMatcher}, {@link OrMatcher}, {@link NotMatcher}, {@link ConditionalTemplateConfigurationFactory},
* {@link MergingTemplateConfigurationFactory}, {@link FirstMatchTemplateConfigurationFactory},
@@ -2216,8 +2214,6 @@ public class Configurable {
}
} else if ("simple".equalsIgnoreCase(value)) {
setObjectWrapper(_StaticObjectWrappers.SIMPLE_OBJECT_WRAPPER);
- } else if ("beans".equalsIgnoreCase(value)) {
- setObjectWrapper(_StaticObjectWrappers.BEANS_WRAPPER);
} else {
setObjectWrapper((ObjectWrapper) _ObjectBuilderSettingEvaluator.eval(
value, ObjectWrapper.class, false, _SettingEvaluationEnvironment.getCurrent()));
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/_CoreLogs.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_CoreLogs.java b/src/main/java/org/apache/freemarker/core/_CoreLogs.java
index 1ccb915..3523255 100644
--- a/src/main/java/org/apache/freemarker/core/_CoreLogs.java
+++ b/src/main/java/org/apache/freemarker/core/_CoreLogs.java
@@ -27,11 +27,13 @@ import org.slf4j.LoggerFactory;
* access things inside this package that users shouldn't.
*/
public final class _CoreLogs {
-
- public static final Logger RUNTIME = LoggerFactory.getLogger("org.apache.freemarker.core.runtime");
- public static final Logger ATTEMPT = LoggerFactory.getLogger("org.apache.freemarker.core.runtime.attempt");
- public static final Logger SECURITY = LoggerFactory.getLogger("org.apache.freemarker.core.security");
- public static final Logger BEANS_WRAPPER = LoggerFactory.getLogger("org.apache.freemarker.core.model.impl.beans");
+
+ // [FM3] Why "Runtime"? "TemplateProcessing" maybe?
+ public static final Logger RUNTIME = LoggerFactory.getLogger("org.apache.freemarker.core.Runtime");
+ public static final Logger ATTEMPT = LoggerFactory.getLogger("org.apache.freemarker.core.Runtime.Attempt");
+ public static final Logger SECURITY = LoggerFactory.getLogger("org.apache.freemarker.core.Security");
+ public static final Logger OBJECT_WRAPPER = LoggerFactory.getLogger("org.apache.freemarker.core.model" +
+ ".ObjectWrapper");
public static final Logger TEMPLATE_RESOLVER = LoggerFactory.getLogger(
"org.apache.freemarker.core.templateresolver");
public static final Logger DEBUG_SERVER = LoggerFactory.getLogger("org.apache.freemarker.core.debug.server");
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java b/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
index 812aa5a..93ae5ac 100644
--- a/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
+++ b/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
@@ -23,7 +23,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
-import org.apache.freemarker.core.model.impl.beans._MethodUtil;
+import org.apache.freemarker.core.model.impl._MethodUtil;
import org.apache.freemarker.core.util._ClassUtil;
import org.apache.freemarker.core.util._StringUtil;
import org.slf4j.Logger;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
index 9c18db0..97f0ce5 100644
--- a/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
+++ b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
@@ -41,7 +41,7 @@ import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelException;
import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
import org.apache.freemarker.core.model.impl.SimpleObjectWrapper;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
import org.apache.freemarker.core.outputformat.impl.PlainTextOutputFormat;
import org.apache.freemarker.core.outputformat.impl.RTFOutputFormat;
@@ -658,7 +658,7 @@ public class _ObjectBuilderSettingEvaluator {
SHORTHANDS = new HashMap/*<String,String>*/();
addWithSimpleName(SHORTHANDS, DefaultObjectWrapper.class);
- addWithSimpleName(SHORTHANDS, BeansWrapper.class);
+ addWithSimpleName(SHORTHANDS, DefaultObjectWrapper.class);
addWithSimpleName(SHORTHANDS, SimpleObjectWrapper.class);
addWithSimpleName(SHORTHANDS, TemplateConfiguration.class);
@@ -981,7 +981,7 @@ public class _ObjectBuilderSettingEvaluator {
"Failed to call " + cl.getName() + " 0-argument constructor", e);
}
} else {
- BeansWrapper ow = env.getObjectWrapper();
+ DefaultObjectWrapper ow = env.getObjectWrapper();
List/*<TemplateModel>*/ tmArgs = new ArrayList(positionalParamValues.size());
for (int i = 0; i < positionalParamValues.size(); i++) {
try {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java b/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java
index 7e9fb37..f095467 100644
--- a/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java
+++ b/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java
@@ -21,7 +21,7 @@ package org.apache.freemarker.core;
import java.util.Properties;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
/**
* Don't use this; used internally by FreeMarker, might changes without notice.
@@ -31,7 +31,7 @@ public class _SettingEvaluationEnvironment {
private static final ThreadLocal CURRENT = new ThreadLocal();
- private BeansWrapper objectWrapper;
+ private DefaultObjectWrapper objectWrapper;
public static _SettingEvaluationEnvironment getCurrent() {
Object r = CURRENT.get();
@@ -51,9 +51,9 @@ public class _SettingEvaluationEnvironment {
CURRENT.set(previous);
}
- public BeansWrapper getObjectWrapper() {
+ public DefaultObjectWrapper getObjectWrapper() {
if (objectWrapper == null) {
- objectWrapper = new BeansWrapper(Configuration.VERSION_3_0_0);
+ objectWrapper = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
}
return objectWrapper;
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java b/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java
index a321742..19b40cb 100644
--- a/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java
+++ b/src/main/java/org/apache/freemarker/core/model/ObjectWrapper.java
@@ -22,15 +22,15 @@ package org.apache.freemarker.core.model;
import java.util.Map;
import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
/**
* Maps Java objects to the type-system of FreeMarker Template Language (see the {@link TemplateModel}
* interfaces). Thus this is what decides what parts of the Java objects will be accessible in the templates and how.
*
- * <p>For example, with a {@link BeansWrapper} both the items of {@link Map} and the JavaBean properties (the getters)
+ * <p>For example, with a {@link DefaultObjectWrapper} both the items of {@link Map} and the JavaBean properties (the getters)
* of an object are accessible in template uniformly with the {@code myObject.foo} syntax, where "foo" is the map key or
- * the property name. This is because both kind of object is wrapped by {@link BeansWrapper} into a
+ * the property name. This is because both kind of object is wrapped by {@link DefaultObjectWrapper} into a
* {@link TemplateHashModel} implementation that will call {@link Map#get(Object)} or the getter method, transparently
* to the template language.
*
@@ -44,7 +44,7 @@ public interface ObjectWrapper {
*
* @param obj The object to wrap into a {@link TemplateModel}. If it already implements {@link TemplateModel},
* it should just return the object as is. If it's {@code null}, the method should return {@code null}
- * (however, {@link BeansWrapper}, has a legacy option for returning a null model object instead, but it's not
+ * (however, {@link DefaultObjectWrapper}, has a legacy option for returning a null model object instead, but it's not
* a good idea).
*
* @return a {@link TemplateModel} wrapper of the object passed in. To support un-wrapping, you may consider the
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
index d624ede..85adfcb 100644
--- a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
+++ b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModel.java
@@ -29,7 +29,7 @@ import org.apache.freemarker.core.Environment;
/**
* "method" template language data type: Objects that act like functions. The name comes from that their original
- * application was calling Java methods via {@link org.apache.freemarker.core.model.impl.beans.BeansWrapper}.
+ * application was calling Java methods via {@link org.apache.freemarker.core.model.impl.DefaultObjectWrapper}.
*
* <p>In templates they are used like {@code myMethod("foo", "bar")} or {@code myJavaObject.myJavaMethod("foo", "bar")}.
*
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
index fdf7ec0..d06da21 100644
--- a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
+++ b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
@@ -26,7 +26,7 @@ import org.apache.freemarker.core.util.DeepUnwrap;
/**
* "extended method" template language data type: Objects that act like functions. Their main application is calling
- * Java methods via {@link org.apache.freemarker.core.model.impl.beans.BeansWrapper}, but you can implement this interface to create
+ * Java methods via {@link org.apache.freemarker.core.model.impl.DefaultObjectWrapper}, but you can implement this interface to create
* top-level functions too. They are "extended" compared to the deprecated {@link TemplateMethodModel}, which could only
* accept string parameters.
*
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/APIModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/APIModel.java b/src/main/java/org/apache/freemarker/core/model/impl/APIModel.java
new file mode 100644
index 0000000..f5f686d
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/APIModel.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+/**
+ * Exposes the Java API (and properties) of an object.
+ *
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>The exposion level is inherited from the {@link DefaultObjectWrapper}</li>
+ * <li>But methods will always shadow properties and fields with identical name, regardless of {@link DefaultObjectWrapper}
+ * settings</li>
+ * </ul>
+ *
+ * @since 2.3.22
+ */
+final class APIModel extends BeanModel {
+
+ APIModel(Object object, DefaultObjectWrapper wrapper) {
+ super(object, wrapper, false);
+ }
+
+ protected boolean isMethodsShadowItems() {
+ return true;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/ArgumentTypes.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ArgumentTypes.java b/src/main/java/org/apache/freemarker/core/model/impl/ArgumentTypes.java
new file mode 100644
index 0000000..6e16ecd
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ArgumentTypes.java
@@ -0,0 +1,647 @@
+/*
+ * 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.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util.BugException;
+import org.apache.freemarker.core.util._ClassUtil;
+
+/**
+ * The argument types of a method call; usable as cache key.
+ */
+final class ArgumentTypes {
+
+ /**
+ * Conversion difficulty: Lowest; Java Reflection will do it automatically.
+ */
+ private static final int CONVERSION_DIFFICULTY_REFLECTION = 0;
+
+ /**
+ * Conversion difficulty: Medium: Java reflection API won't convert it, FreeMarker has to do it.
+ */
+ private static final int CONVERSION_DIFFICULTY_FREEMARKER = 1;
+
+ /**
+ * Conversion difficulty: Highest; conversion is not possible.
+ */
+ private static final int CONVERSION_DIFFICULTY_IMPOSSIBLE = 2;
+
+ /**
+ * The types of the arguments; for varags this contains the exploded list (not the array).
+ */
+ private final Class<?>[] types;
+
+ /**
+ * @param args The actual arguments. A varargs argument should be present exploded, no as an array.
+ */
+ ArgumentTypes(Object[] args) {
+ int ln = args.length;
+ Class<?>[] typesTmp = new Class[ln];
+ for (int i = 0; i < ln; ++i) {
+ Object arg = args[i];
+ typesTmp[i] = arg == null
+ ? Null.class
+ : arg.getClass();
+ }
+
+ // `typesTmp` is used so the array is only modified before it's stored in the final `types` field (see JSR-133)
+ types = typesTmp;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ for (Class<?> type : types) {
+ hash ^= type.hashCode();
+ }
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof ArgumentTypes) {
+ ArgumentTypes cs = (ArgumentTypes) o;
+ if (cs.types.length != types.length) {
+ return false;
+ }
+ for (int i = 0; i < types.length; ++i) {
+ if (cs.types[i] != types[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return Possibly {@link EmptyCallableMemberDescriptor#NO_SUCH_METHOD} or
+ * {@link EmptyCallableMemberDescriptor#AMBIGUOUS_METHOD}.
+ */
+ MaybeEmptyCallableMemberDescriptor getMostSpecific(
+ List<ReflectionCallableMemberDescriptor> memberDescs, boolean varArg) {
+ LinkedList<CallableMemberDescriptor> applicables = getApplicables(memberDescs, varArg);
+ if (applicables.isEmpty()) {
+ return EmptyCallableMemberDescriptor.NO_SUCH_METHOD;
+ }
+ if (applicables.size() == 1) {
+ return applicables.getFirst();
+ }
+
+ LinkedList<CallableMemberDescriptor> maximals = new LinkedList<>();
+ for (CallableMemberDescriptor applicable : applicables) {
+ boolean lessSpecific = false;
+ for (Iterator<CallableMemberDescriptor> maximalsIter = maximals.iterator();
+ maximalsIter.hasNext(); ) {
+ CallableMemberDescriptor maximal = maximalsIter.next();
+ final int cmpRes = compareParameterListPreferability(
+ applicable.getParamTypes(), maximal.getParamTypes(), varArg);
+ if (cmpRes > 0) {
+ maximalsIter.remove();
+ } else if (cmpRes < 0) {
+ lessSpecific = true;
+ }
+ }
+ if (!lessSpecific) {
+ maximals.addLast(applicable);
+ }
+ }
+ if (maximals.size() > 1) {
+ return EmptyCallableMemberDescriptor.AMBIGUOUS_METHOD;
+ }
+ return maximals.getFirst();
+ }
+
+ /**
+ * Tells if among the parameter list of two methods, which one fits this argument list better.
+ * This method assumes that the parameter lists are applicable to this argument lists; if that's not ensured,
+ * what the result will be is undefined.
+ *
+ * <p>The decision is made by comparing the preferability of each parameter types of the same position in a loop.
+ * At the end, the parameter list with the more preferred parameters will be the preferred one. If both parameter
+ * lists has the same amount of preferred parameters, the one that has the first (lower index) preferred parameter
+ * is the preferred one. Otherwise the two parameter list are considered to be equal in terms of preferability.
+ *
+ * <p>If there's no numerical conversion involved, the preferability of two parameter types is decided on how
+ * specific their types are. For example, {@code String} is more specific than {@link Object} (because
+ * {@code Object.class.isAssignableFrom(String.class)}-s), and so {@code String} is preferred. Primitive
+ * types are considered to be more specific than the corresponding boxing class (like {@code boolean} is more
+ * specific than {@code Boolean}, because the former can't store {@code null}). The preferability decision gets
+ * trickier when there's a possibility of numerical conversion from the actual argument type to the type of some of
+ * the parameters. If such conversion is only possible for one of the competing parameter types, that parameter
+ * automatically wins. If it's possible for both, {@link OverloadedNumberUtil#getArgumentConversionPrice} will
+ * be used to calculate the conversion "price", and the parameter type with lowest price wins. There are also
+ * a twist with array-to-list and list-to-array conversions; we try to avoid those, so the parameter where such
+ * conversion isn't needed will always win.
+ *
+ * @param paramTypes1 The parameter types of one of the competing methods
+ * @param paramTypes2 The parameter types of the other competing method
+ * @param varArg Whether these competing methods are varargs methods.
+ * @return More than 0 if the first parameter list is preferred, less then 0 if the other is preferred,
+ * 0 if there's no decision
+ */
+ int compareParameterListPreferability(Class<?>[] paramTypes1, Class<?>[] paramTypes2, boolean varArg) {
+ final int argTypesLen = types.length;
+ final int paramTypes1Len = paramTypes1.length;
+ final int paramTypes2Len = paramTypes2.length;
+ //assert varArg || paramTypes1Len == paramTypes2Len;
+
+ int paramList1WeakWinCnt = 0;
+ int paramList2WeakWinCnt = 0;
+ int paramList1WinCnt = 0;
+ int paramList2WinCnt = 0;
+ int paramList1StrongWinCnt = 0;
+ int paramList2StrongWinCnt = 0;
+ int paramList1VeryStrongWinCnt = 0;
+ int paramList2VeryStrongWinCnt = 0;
+ int firstWinerParamList = 0;
+ for (int i = 0; i < argTypesLen; i++) {
+ final Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, i, varArg);
+ final Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, i, varArg);
+
+ final int winerParam; // 1 => paramType1; -1 => paramType2; 0 => draw
+ if (paramType1 == paramType2) {
+ winerParam = 0;
+ } else {
+ final Class<?> argType = types[i];
+ final boolean argIsNum = Number.class.isAssignableFrom(argType);
+
+ final int numConvPrice1;
+ if (argIsNum && _ClassUtil.isNumerical(paramType1)) {
+ final Class<?> nonPrimParamType1 = paramType1.isPrimitive()
+ ? _ClassUtil.primitiveClassToBoxingClass(paramType1) : paramType1;
+ numConvPrice1 = OverloadedNumberUtil.getArgumentConversionPrice(argType, nonPrimParamType1);
+ } else {
+ numConvPrice1 = Integer.MAX_VALUE;
+ }
+ // numConvPrice1 is Integer.MAX_VALUE if either:
+ // - argType and paramType1 aren't both numerical
+ // - FM doesn't know some of the numerical types, or the conversion between them is not allowed
+
+ final int numConvPrice2;
+ if (argIsNum && _ClassUtil.isNumerical(paramType2)) {
+ final Class<?> nonPrimParamType2 = paramType2.isPrimitive()
+ ? _ClassUtil.primitiveClassToBoxingClass(paramType2) : paramType2;
+ numConvPrice2 = OverloadedNumberUtil.getArgumentConversionPrice(argType, nonPrimParamType2);
+ } else {
+ numConvPrice2 = Integer.MAX_VALUE;
+ }
+
+ if (numConvPrice1 == Integer.MAX_VALUE) {
+ if (numConvPrice2 == Integer.MAX_VALUE) { // No numerical conversions anywhere
+ // List to array conversions (unwrapping sometimes makes a List instead of an array)
+ if (List.class.isAssignableFrom(argType)
+ && (paramType1.isArray() || paramType2.isArray())) {
+ if (paramType1.isArray()) {
+ if (paramType2.isArray()) { // both paramType1 and paramType2 are arrays
+ int r = compareParameterListPreferability_cmpTypeSpecificty(
+ paramType1.getComponentType(), paramType2.getComponentType());
+ // Because we don't know if the List items are instances of the component
+ // type or not, we prefer the safer choice, which is the more generic array:
+ if (r > 0) {
+ winerParam = 2;
+ paramList2StrongWinCnt++;
+ } else if (r < 0) {
+ winerParam = 1;
+ paramList1StrongWinCnt++;
+ } else {
+ winerParam = 0;
+ }
+ } else { // paramType1 is array, paramType2 isn't
+ // Avoid List to array conversion if the other way makes any sense:
+ if (Collection.class.isAssignableFrom(paramType2)) {
+ winerParam = 2;
+ paramList2StrongWinCnt++;
+ } else {
+ winerParam = 1;
+ paramList1WeakWinCnt++;
+ }
+ }
+ } else { // paramType2 is array, paramType1 isn't
+ // Avoid List to array conversion if the other way makes any sense:
+ if (Collection.class.isAssignableFrom(paramType1)) {
+ winerParam = 1;
+ paramList1StrongWinCnt++;
+ } else {
+ winerParam = 2;
+ paramList2WeakWinCnt++;
+ }
+ }
+ } else if (argType.isArray()
+ && (List.class.isAssignableFrom(paramType1)
+ || List.class.isAssignableFrom(paramType2))) {
+ // Array to List conversions (unwrapping sometimes makes an array instead of a List)
+ if (List.class.isAssignableFrom(paramType1)) {
+ if (List.class.isAssignableFrom(paramType2)) {
+ // Both paramType1 and paramType2 extends List
+ winerParam = 0;
+ } else {
+ // Only paramType1 extends List
+ winerParam = 2;
+ paramList2VeryStrongWinCnt++;
+ }
+ } else {
+ // Only paramType2 extends List
+ winerParam = 1;
+ paramList1VeryStrongWinCnt++;
+ }
+ } else { // No list to/from array conversion
+ final int r = compareParameterListPreferability_cmpTypeSpecificty(
+ paramType1, paramType2);
+ if (r > 0) {
+ winerParam = 1;
+ if (r > 1) {
+ paramList1WinCnt++;
+ } else {
+ paramList1WeakWinCnt++;
+ }
+ } else if (r < 0) {
+ winerParam = -1;
+ if (r < -1) {
+ paramList2WinCnt++;
+ } else {
+ paramList2WeakWinCnt++;
+ }
+ } else {
+ winerParam = 0;
+ }
+ }
+ } else { // No num. conv. of param1, num. conv. of param2
+ winerParam = -1;
+ paramList2WinCnt++;
+ }
+ } else if (numConvPrice2 == Integer.MAX_VALUE) { // Num. conv. of param1, not of param2
+ winerParam = 1;
+ paramList1WinCnt++;
+ } else { // Num. conv. of both param1 and param2
+ if (numConvPrice1 != numConvPrice2) {
+ if (numConvPrice1 < numConvPrice2) {
+ winerParam = 1;
+ if (numConvPrice1 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE
+ && numConvPrice2 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) {
+ paramList1StrongWinCnt++;
+ } else {
+ paramList1WinCnt++;
+ }
+ } else {
+ winerParam = -1;
+ if (numConvPrice2 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE
+ && numConvPrice1 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) {
+ paramList2StrongWinCnt++;
+ } else {
+ paramList2WinCnt++;
+ }
+ }
+ } else {
+ winerParam = (paramType1.isPrimitive() ? 1 : 0) - (paramType2.isPrimitive() ? 1 : 0);
+ if (winerParam == 1) paramList1WeakWinCnt++;
+ else if (winerParam == -1) paramList2WeakWinCnt++;
+ }
+ }
+ } // when paramType1 != paramType2
+
+ if (firstWinerParamList == 0 && winerParam != 0) {
+ firstWinerParamList = winerParam;
+ }
+ } // for each parameter types
+
+ if (paramList1VeryStrongWinCnt != paramList2VeryStrongWinCnt) {
+ return paramList1VeryStrongWinCnt - paramList2VeryStrongWinCnt;
+ } else if (paramList1StrongWinCnt != paramList2StrongWinCnt) {
+ return paramList1StrongWinCnt - paramList2StrongWinCnt;
+ } else if (paramList1WinCnt != paramList2WinCnt) {
+ return paramList1WinCnt - paramList2WinCnt;
+ } else if (paramList1WeakWinCnt != paramList2WeakWinCnt) {
+ return paramList1WeakWinCnt - paramList2WeakWinCnt;
+ } else if (firstWinerParamList != 0) { // paramList1WinCnt == paramList2WinCnt
+ return firstWinerParamList;
+ } else { // still undecided
+ if (varArg) {
+ if (paramTypes1Len == paramTypes2Len) {
+ // If we had a 0-length varargs array in both methods, we also compare the types at the
+ // index of the varargs parameter, like if we had a single varargs argument. However, this
+ // time we don't have an argument type, so we can only decide based on type specificity:
+ if (argTypesLen == paramTypes1Len - 1) {
+ Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, argTypesLen, true);
+ Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, argTypesLen, true);
+ if (_ClassUtil.isNumerical(paramType1) && _ClassUtil.isNumerical(paramType2)) {
+ int r = OverloadedNumberUtil.compareNumberTypeSpecificity(paramType1, paramType2);
+ if (r != 0) return r;
+ // falls through
+ }
+ return compareParameterListPreferability_cmpTypeSpecificty(paramType1, paramType2);
+ } else {
+ return 0;
+ }
+ } else {
+ // The method with more oms parameters wins:
+ return paramTypes1Len - paramTypes2Len;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ /**
+ * Trivial comparison of type specificities; unaware of numerical conversions.
+ *
+ * @return Less-than-0, 0, or more-than-0 depending on which side is more specific. The absolute value is 1 if
+ * the difference is only in primitive VS non-primitive, more otherwise.
+ */
+ private int compareParameterListPreferability_cmpTypeSpecificty(
+ final Class<?> paramType1, final Class<?> paramType2) {
+ // The more specific (smaller) type wins.
+
+ final Class<?> nonPrimParamType1 = paramType1.isPrimitive()
+ ? _ClassUtil.primitiveClassToBoxingClass(paramType1) : paramType1;
+ final Class<?> nonPrimParamType2 = paramType2.isPrimitive()
+ ? _ClassUtil.primitiveClassToBoxingClass(paramType2) : paramType2;
+
+ if (nonPrimParamType1 == nonPrimParamType2) {
+ if (nonPrimParamType1 != paramType1) {
+ if (nonPrimParamType2 != paramType2) {
+ return 0; // identical prim. types; shouldn't ever be reached
+ } else {
+ return 1; // param1 is prim., param2 is non prim.
+ }
+ } else if (nonPrimParamType2 != paramType2) {
+ return -1; // param1 is non-prim., param2 is prim.
+ } else {
+ return 0; // identical non-prim. types
+ }
+ } else if (nonPrimParamType2.isAssignableFrom(nonPrimParamType1)) {
+ return 2;
+ } else if (nonPrimParamType1.isAssignableFrom(nonPrimParamType2)) {
+ return -2;
+ } if (nonPrimParamType1 == Character.class && nonPrimParamType2.isAssignableFrom(String.class)) {
+ return 2; // A character is a 1 long string in FTL, so we pretend that it's a String subtype.
+ } if (nonPrimParamType2 == Character.class && nonPrimParamType1.isAssignableFrom(String.class)) {
+ return -2;
+ } else {
+ return 0; // unrelated types
+ }
+ }
+
+ private static Class<?> getParamType(Class<?>[] paramTypes, int paramTypesLen, int i, boolean varArg) {
+ return varArg && i >= paramTypesLen - 1
+ ? paramTypes[paramTypesLen - 1].getComponentType()
+ : paramTypes[i];
+ }
+
+ /**
+ * Returns all methods that are applicable to actual
+ * parameter types represented by this ArgumentTypes object.
+ */
+ LinkedList<CallableMemberDescriptor> getApplicables(
+ List<ReflectionCallableMemberDescriptor> memberDescs, boolean varArg) {
+ LinkedList<CallableMemberDescriptor> applicables = new LinkedList<>();
+ for (ReflectionCallableMemberDescriptor memberDesc : memberDescs) {
+ int difficulty = isApplicable(memberDesc, varArg);
+ if (difficulty != CONVERSION_DIFFICULTY_IMPOSSIBLE) {
+ if (difficulty == CONVERSION_DIFFICULTY_REFLECTION) {
+ applicables.add(memberDesc);
+ } else if (difficulty == CONVERSION_DIFFICULTY_FREEMARKER) {
+ applicables.add(new SpecialConversionCallableMemberDescriptor(memberDesc));
+ } else {
+ throw new BugException();
+ }
+ }
+ }
+ return applicables;
+ }
+
+ /**
+ * Returns if the supplied method is applicable to actual
+ * parameter types represented by this ArgumentTypes object, also tells
+ * how difficult that conversion is.
+ *
+ * @return One of the <tt>CONVERSION_DIFFICULTY_...</tt> constants.
+ */
+ private int isApplicable(ReflectionCallableMemberDescriptor memberDesc, boolean varArg) {
+ final Class<?>[] paramTypes = memberDesc.getParamTypes();
+ final int cl = types.length;
+ final int fl = paramTypes.length - (varArg ? 1 : 0);
+ if (varArg) {
+ if (cl < fl) {
+ return CONVERSION_DIFFICULTY_IMPOSSIBLE;
+ }
+ } else {
+ if (cl != fl) {
+ return CONVERSION_DIFFICULTY_IMPOSSIBLE;
+ }
+ }
+
+ int maxDifficulty = 0;
+ for (int i = 0; i < fl; ++i) {
+ int difficulty = isMethodInvocationConvertible(paramTypes[i], types[i]);
+ if (difficulty == CONVERSION_DIFFICULTY_IMPOSSIBLE) {
+ return CONVERSION_DIFFICULTY_IMPOSSIBLE;
+ }
+ if (maxDifficulty < difficulty) {
+ maxDifficulty = difficulty;
+ }
+ }
+ if (varArg) {
+ Class<?> varArgParamType = paramTypes[fl].getComponentType();
+ for (int i = fl; i < cl; ++i) {
+ int difficulty = isMethodInvocationConvertible(varArgParamType, types[i]);
+ if (difficulty == CONVERSION_DIFFICULTY_IMPOSSIBLE) {
+ return CONVERSION_DIFFICULTY_IMPOSSIBLE;
+ }
+ if (maxDifficulty < difficulty) {
+ maxDifficulty = difficulty;
+ }
+ }
+ }
+ return maxDifficulty;
+ }
+
+ /**
+ * Determines whether a type is convertible to another type via
+ * method invocation conversion, and if so, what kind of conversion is needed.
+ * It treates the object type counterpart of primitive types as if they were the primitive types
+ * (that is, a Boolean actual parameter type matches boolean primitive formal type). This behavior
+ * is because this method is used to determine applicable methods for
+ * an actual parameter list, and primitive types are represented by
+ * their object duals in reflective method calls.
+ * @param formal the parameter type to which the actual
+ * parameter type should be convertible; possibly a primitive type
+ * @param actual the argument type; not a primitive type, maybe {@link Null}.
+ *
+ * @return One of the <tt>CONVERSION_DIFFICULTY_...</tt> constants.
+ */
+ private int isMethodInvocationConvertible(final Class<?> formal, final Class<?> actual) {
+ // Check for identity or widening reference conversion
+ if (formal.isAssignableFrom(actual) && actual != CharacterOrString.class) {
+ return CONVERSION_DIFFICULTY_REFLECTION;
+ } else {
+ final Class<?> formalNP;
+ if (formal.isPrimitive()) {
+ if (actual == Null.class) {
+ return CONVERSION_DIFFICULTY_IMPOSSIBLE;
+ }
+
+ formalNP = _ClassUtil.primitiveClassToBoxingClass(formal);
+ if (actual == formalNP) {
+ // Character and char, etc.
+ return CONVERSION_DIFFICULTY_REFLECTION;
+ }
+ } else { // formal is non-primitive
+ if (actual == Null.class) {
+ return CONVERSION_DIFFICULTY_REFLECTION;
+ }
+
+ formalNP = formal;
+ }
+ if (Number.class.isAssignableFrom(actual) && Number.class.isAssignableFrom(formalNP)) {
+ return OverloadedNumberUtil.getArgumentConversionPrice(actual, formalNP) == Integer.MAX_VALUE
+ ? CONVERSION_DIFFICULTY_IMPOSSIBLE : CONVERSION_DIFFICULTY_REFLECTION;
+ } else if (formal.isArray()) {
+ // DefaultObjectWrapper method/constructor calls convert from List to array automatically
+ return List.class.isAssignableFrom(actual)
+ ? CONVERSION_DIFFICULTY_FREEMARKER : CONVERSION_DIFFICULTY_IMPOSSIBLE;
+ } else if (actual.isArray() && formal.isAssignableFrom(List.class)) {
+ // DefaultObjectWrapper method/constructor calls convert from array to List automatically
+ return CONVERSION_DIFFICULTY_FREEMARKER;
+ } else if (actual == CharacterOrString.class
+ && (formal.isAssignableFrom(String.class)
+ || formal.isAssignableFrom(Character.class) || formal == char.class)) {
+ return CONVERSION_DIFFICULTY_FREEMARKER;
+ } else {
+ return CONVERSION_DIFFICULTY_IMPOSSIBLE;
+ }
+ }
+ }
+
+ /**
+ * Symbolizes the class of null (it's missing from Java).
+ */
+ private static class Null {
+
+ // Can't be instantiated
+ private Null() { }
+
+ }
+
+ /**
+ * Used instead of {@link ReflectionCallableMemberDescriptor} when the method is only applicable
+ * ({@link #isApplicable}) with conversion that Java reflection won't do. It delegates to a
+ * {@link ReflectionCallableMemberDescriptor}, but it adds the necessary conversions to the invocation methods.
+ */
+ private static final class SpecialConversionCallableMemberDescriptor extends CallableMemberDescriptor {
+
+ private final ReflectionCallableMemberDescriptor callableMemberDesc;
+
+ SpecialConversionCallableMemberDescriptor(ReflectionCallableMemberDescriptor callableMemberDesc) {
+ this.callableMemberDesc = callableMemberDesc;
+ }
+
+ @Override
+ TemplateModel invokeMethod(DefaultObjectWrapper ow, Object obj, Object[] args) throws TemplateModelException,
+ InvocationTargetException, IllegalAccessException {
+ convertArgsToReflectionCompatible(ow, args);
+ return callableMemberDesc.invokeMethod(ow, obj, args);
+ }
+
+ @Override
+ Object invokeConstructor(DefaultObjectWrapper ow, Object[] args) throws IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException, TemplateModelException {
+ convertArgsToReflectionCompatible(ow, args);
+ return callableMemberDesc.invokeConstructor(ow, args);
+ }
+
+ @Override
+ String getDeclaration() {
+ return callableMemberDesc.getDeclaration();
+ }
+
+ @Override
+ boolean isConstructor() {
+ return callableMemberDesc.isConstructor();
+ }
+
+ @Override
+ boolean isStatic() {
+ return callableMemberDesc.isStatic();
+ }
+
+ @Override
+ boolean isVarargs() {
+ return callableMemberDesc.isVarargs();
+ }
+
+ @Override
+ Class<?>[] getParamTypes() {
+ return callableMemberDesc.getParamTypes();
+ }
+
+ @Override
+ String getName() {
+ return callableMemberDesc.getName();
+ }
+
+ private void convertArgsToReflectionCompatible(DefaultObjectWrapper ow, Object[] args) throws TemplateModelException {
+ Class<?>[] paramTypes = callableMemberDesc.getParamTypes();
+ int ln = paramTypes.length;
+ for (int i = 0; i < ln; i++) {
+ Class<?> paramType = paramTypes[i];
+ final Object arg = args[i];
+ if (arg == null) continue;
+
+ // Handle conversion between List and array types, in both directions. Java reflection won't do such
+ // conversion, so we have to.
+ // Most reflection-incompatible conversions were already addressed by the unwrapping. The reason
+ // this one isn't is that for overloaded methods the hint of a given parameter position is often vague,
+ // so we may end up with a List even if some parameter types at that position are arrays (remember, we
+ // have to chose one unwrapping target type, despite that we have many possible overloaded methods), or
+ // the other way around (that happens when AdapterTemplateMoldel returns an array).
+ // Later, the overloaded method selection will assume that a List argument is applicable to an array
+ // parameter, and that an array argument is applicable to a List parameter, so we end up with this
+ // situation.
+ if (paramType.isArray() && arg instanceof List) {
+ args[i] = ow.listToArray((List<?>) arg, paramType, null);
+ }
+ if (arg.getClass().isArray() && paramType.isAssignableFrom(List.class)) {
+ args[i] = ow.arrayToList(arg);
+ }
+
+ // Handle the conversion from CharacterOrString to Character or String:
+ if (arg instanceof CharacterOrString) {
+ if (paramType == Character.class || paramType == char.class
+ || (!paramType.isAssignableFrom(String.class)
+ && paramType.isAssignableFrom(Character.class))) {
+ args[i] = Character.valueOf(((CharacterOrString) arg).getAsChar());
+ } else {
+ args[i] = ((CharacterOrString) arg).getAsString();
+ }
+ }
+ }
+ }
+
+ }
+
+}
\ 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/ArrayModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java b/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java
new file mode 100644
index 0000000..12ef9f0
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ArrayModel.java
@@ -0,0 +1,126 @@
+/*
+ * 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.lang.reflect.Array;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+
+/**
+ * <p>A class that will wrap an arbitrary array into {@link TemplateCollectionModel}
+ * and {@link TemplateSequenceModel} interfaces. It supports element retrieval through the <tt>array[index]</tt>
+ * syntax and can be iterated as a list.
+ */
+public class ArrayModel
+extends
+ BeanModel
+implements
+ TemplateCollectionModel,
+ TemplateSequenceModel {
+ static final ModelFactory FACTORY =
+ new ModelFactory()
+ {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new ArrayModel(object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+ // Cached length of the array
+ private final int length;
+
+ /**
+ * Creates a new model that wraps the specified array object.
+ * @param array the array object to wrap into a model.
+ * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+ * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+ * model gains many attributes from its wrapper, including the caching
+ * behavior, method exposure level, method-over-item shadowing policy etc.
+ * @throws IllegalArgumentException if the passed object is not a Java array.
+ */
+ public ArrayModel(Object array, DefaultObjectWrapper wrapper) {
+ super(array, wrapper);
+ Class clazz = array.getClass();
+ if (!clazz.isArray())
+ throw new IllegalArgumentException("Object is not an array, it's " + array.getClass().getName());
+ length = Array.getLength(array);
+ }
+
+
+ @Override
+ public TemplateModelIterator iterator() {
+ return new Iterator();
+ }
+
+ @Override
+ public TemplateModel get(int index)
+ throws TemplateModelException {
+ try {
+ return wrap(Array.get(object, index));
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+// throw new TemplateModelException("Index out of bounds: " + index);
+ }
+ }
+
+ private class Iterator
+ implements
+ TemplateSequenceModel,
+ TemplateModelIterator {
+ private int position = 0;
+
+ @Override
+ public boolean hasNext() {
+ return position < length;
+ }
+
+ @Override
+ public TemplateModel get(int index)
+ throws TemplateModelException {
+ return ArrayModel.this.get(index);
+ }
+
+ @Override
+ public TemplateModel next()
+ throws TemplateModelException {
+ return position < length ? get(position++) : null;
+ }
+
+ @Override
+ public int size() {
+ return ArrayModel.this.size();
+ }
+ }
+
+ @Override
+ public int size() {
+ return length;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return length == 0;
+ }
+}
[05/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/ParameterListPreferabilityTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/ParameterListPreferabilityTest.java b/src/test/java/org/apache/freemarker/core/model/impl/ParameterListPreferabilityTest.java
new file mode 100644
index 0000000..2c22dc6
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/ParameterListPreferabilityTest.java
@@ -0,0 +1,445 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.freemarker.core.util._NumberUtil;
+
+import junit.framework.TestCase;
+
+@SuppressWarnings("boxing")
+public class ParameterListPreferabilityTest extends TestCase {
+
+ public ParameterListPreferabilityTest(String name) {
+ super(name);
+ }
+
+ public void testNumberical() {
+ // Note: the signature lists consists of the same elements, only their order changes depending on the type
+ // of the argument value.
+
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { byte.class },
+ new Class[] { Byte.class },
+ new Class[] { short.class },
+ new Class[] { Short.class },
+ new Class[] { int.class },
+ new Class[] { Integer.class },
+ new Class[] { long.class },
+ new Class[] { Long.class },
+ new Class[] { BigInteger.class },
+ new Class[] { float.class },
+ new Class[] { Float.class },
+ new Class[] { double.class },
+ new Class[] { Double.class },
+ new Class[] { BigDecimal.class },
+ new Class[] { Number.class },
+ new Class[] { Serializable.class },
+ new Class[] { Object.class }
+ },
+ new Object[] { (byte) 1 });
+
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { BigDecimal.class },
+ new Class[] { BigInteger.class },
+ new Class[] { int.class },
+ new Class[] { Integer.class },
+ new Class[] { long.class },
+ new Class[] { Long.class },
+ new Class[] { double.class },
+ new Class[] { Double.class },
+ new Class[] { float.class },
+ new Class[] { Float.class },
+ new Class[] { short.class },
+ new Class[] { Short.class },
+ new Class[] { byte.class },
+ new Class[] { Byte.class },
+ new Class[] { Number.class },
+ new Class[] { Serializable.class },
+ new Class[] { Object.class },
+ },
+ new Object[] { new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal("1")) });
+
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { BigDecimal.class },
+ new Class[] { double.class },
+ new Class[] { Double.class },
+ new Class[] { float.class },
+ new Class[] { Float.class },
+ new Class[] { BigInteger.class },
+ new Class[] { int.class },
+ new Class[] { Integer.class },
+ new Class[] { long.class },
+ new Class[] { Long.class },
+ new Class[] { short.class },
+ new Class[] { Short.class },
+ new Class[] { byte.class },
+ new Class[] { Byte.class },
+ new Class[] { Number.class },
+ new Class[] { Serializable.class },
+ new Class[] { Object.class },
+ },
+ new Object[] { new BigDecimal("1") /* possibly non-integer */ });
+
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { float.class },
+ new Class[] { Float.class },
+ new Class[] { double.class },
+ new Class[] { Double.class },
+ new Class[] { BigDecimal.class },
+ new Class[] { int.class },
+ new Class[] { Integer.class },
+ new Class[] { long.class },
+ new Class[] { Long.class },
+ new Class[] { short.class },
+ new Class[] { Short.class },
+ new Class[] { byte.class },
+ new Class[] { Byte.class },
+ new Class[] { BigInteger.class },
+ new Class[] { Number.class },
+ new Class[] { Serializable.class },
+ new Class[] { Object.class },
+ },
+ new Object[] { new OverloadedNumberUtil.FloatOrByte(1f, (byte) 1) });
+
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { int.class },
+ new Class[] { Integer.class },
+ new Class[] { long.class },
+ new Class[] { Long.class },
+ new Class[] { BigInteger.class },
+ new Class[] { double.class },
+ new Class[] { Double.class },
+ new Class[] { BigDecimal.class },
+ new Class[] { short.class },
+ new Class[] { Short.class },
+ new Class[] { float.class },
+ new Class[] { Float.class },
+
+ // Two incompatibles removed, would be removed in reality:
+ new Class[] { byte.class },
+ new Class[] { Byte.class },
+
+ new Class[] { Number.class },
+ new Class[] { Serializable.class },
+ new Class[] { Object.class }
+ },
+ new Object[] { new OverloadedNumberUtil.IntegerOrShort(1, (short) 1) });
+
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { long.class },
+ new Class[] { Long.class },
+ new Class[] { BigInteger.class },
+ new Class[] { BigDecimal.class },
+ new Class[] { double.class },
+ new Class[] { Double.class },
+ new Class[] { float.class },
+ new Class[] { Float.class },
+ // skip byte and short as the would be equal with int (all invalid target types)
+ new Class[] { int.class }, // In reality, this and Integer are removed as not-applicable overloads
+ new Class[] { Integer.class },
+ new Class[] { Number.class },
+ new Class[] { Serializable.class },
+ new Class[] { Object.class }
+ },
+ new Object[] { 1L });
+
+ // Undecidable comparisons:
+
+ testAllCmpPermutationsEqu(
+ new Class[][] {
+ new Class[] { Byte.class },
+ new Class[] { Short.class },
+ new Class[] { Integer.class },
+ new Class[] { Long.class },
+ new Class[] { BigInteger.class },
+ new Class[] { Float.class },
+ },
+ new Object[] { 1.0 });
+
+ testAllCmpPermutationsEqu(
+ new Class[][] {
+ new Class[] { byte.class },
+ new Class[] { short.class },
+ new Class[] { int.class },
+ new Class[] { long.class },
+ new Class[] { float.class },
+ },
+ new Object[] { 1.0 });
+ }
+
+ public void testPrimitiveIsMoreSpecific() {
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { boolean.class },
+ new Class[] { Boolean.class }
+ },
+ new Object[] { true });
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { char.class },
+ new Class[] { Character.class }
+ },
+ new Object[] { 'x' });
+ }
+
+ public void testCharIsMoreSpecificThanString() {
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { char.class },
+ new Class[] { Character.class },
+ new Class[] { String.class },
+ new Class[] { CharSequence.class }
+ },
+ new Object[] { "s" });
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { char.class },
+ new Class[] { Character.class },
+ new Class[] { String.class }
+ },
+ new Object[] { 'c' });
+ }
+
+ public void testClassHierarchy() {
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { LinkedHashMap.class },
+ new Class[] { HashMap.class },
+ new Class[] { Map.class },
+ new Class[] { Object.class }
+ },
+ new Object[] { new LinkedHashMap() });
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { LinkedHashMap.class },
+ new Class[] { Cloneable.class },
+ new Class[] { Object.class }
+ },
+ new Object[] { new LinkedHashMap() });
+ }
+
+ public void testNumericalWithNonNumerical() {
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { int.class },
+ new Class[] { Integer.class },
+ new Class[] { Comparable.class },
+ new Class[] { Object.class },
+ },
+ new Object[] { 1 });
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { int.class },
+ new Class[] { Integer.class },
+ new Class[] { char.class },
+ new Class[] { Character.class },
+ },
+ new Object[] { 1 });
+ }
+
+ public void testUnrelated() {
+ testAllCmpPermutationsEqu(
+ new Class[][] {
+ new Class[] { Serializable.class },
+ new Class[] { CharSequence.class },
+ new Class[] { Comparable.class }
+ },
+ new Object[] { "s" });
+
+ testAllCmpPermutationsEqu(
+ new Class[][] {
+ new Class[] { HashMap.class },
+ new Class[] { TreeMap.class }
+ },
+ new Object[] { null });
+ }
+
+ public void testMultiParameter() {
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { String.class, String.class, String.class },
+
+ new Class[] { String.class, String.class, Object.class },
+ new Class[] { String.class, Object.class, String.class },
+ new Class[] { Object.class, String.class, String.class },
+
+ new Class[] { String.class, Object.class, Object.class },
+ new Class[] { Object.class, String.class, Object.class },
+ new Class[] { Object.class, Object.class, String.class },
+
+ new Class[] { Object.class, Object.class, Object.class },
+ },
+ new Object[] { "a", "b", "c" });
+
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { String.class, String.class },
+ new Class[] { String.class, Object.class },
+ new Class[] { CharSequence.class, CharSequence.class },
+ new Class[] { CharSequence.class, Object.class },
+ new Class[] { Object.class, String.class },
+ new Class[] { Object.class, CharSequence.class },
+ new Class[] { Object.class, Object.class },
+ },
+ new Object[] { "a", "b" });
+
+ // Subclassing is more important than primitive-VS-boxed:
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { boolean.class, boolean.class, boolean.class, String.class },
+ new Class[] { Boolean.class, boolean.class, boolean.class, String.class },
+ new Class[] { boolean.class, Boolean.class, Boolean.class, String.class },
+ new Class[] { Boolean.class, boolean.class, Boolean.class, String.class },
+ new Class[] { Boolean.class, Boolean.class, boolean.class, String.class },
+ new Class[] { Boolean.class, Boolean.class, Boolean.class, String.class },
+ new Class[] { Boolean.class, Boolean.class, Boolean.class, CharSequence.class },
+ new Class[] { boolean.class, boolean.class, boolean.class, Object.class },
+ new Class[] { Boolean.class, boolean.class, boolean.class, Object.class },
+ new Class[] { boolean.class, Boolean.class, Boolean.class, Object.class },
+ new Class[] { Boolean.class, boolean.class, Boolean.class, Object.class },
+ new Class[] { Boolean.class, Boolean.class, boolean.class, Object.class },
+ new Class[] { Boolean.class, Boolean.class, Boolean.class, Object.class },
+ },
+ new Object[] { true, false, true, "a" });
+
+ // Subclassing is more important than primitive-VS-boxed:
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { int.class, int.class, int.class, String.class },
+ new Class[] { Integer.class, int.class, int.class, String.class },
+ new Class[] { int.class, Integer.class, Integer.class, String.class },
+ new Class[] { Integer.class, int.class, Integer.class, String.class },
+ new Class[] { Integer.class, Integer.class, int.class, String.class },
+ new Class[] { Integer.class, Integer.class, Integer.class, String.class },
+ new Class[] { Integer.class, Integer.class, Integer.class, CharSequence.class },
+ new Class[] { int.class, int.class, int.class, Object.class },
+ new Class[] { Integer.class, int.class, int.class, Object.class },
+ new Class[] { int.class, Integer.class, Integer.class, Object.class },
+ new Class[] { Integer.class, int.class, Integer.class, Object.class },
+ new Class[] { Integer.class, Integer.class, int.class, Object.class },
+ new Class[] { Integer.class, Integer.class, Integer.class, Object.class },
+ },
+ new Object[] { 1, 2, 3, "a" });
+ }
+
+ public void testVarargs() {
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { String.class, String[].class },
+ new Class[] { String.class, CharSequence[].class },
+ new Class[] { String.class, Object[].class },
+ },
+ new Object[] { "a", "b", "c" },
+ true);
+
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { String.class, int[].class },
+ new Class[] { String.class, Integer[].class },
+ new Class[] { String.class, long[].class },
+ new Class[] { String.class, Long[].class },
+ new Class[] { String.class, double[].class },
+ new Class[] { String.class, Double[].class },
+ new Class[] { String.class, Serializable[].class },
+ new Class[] { String.class, Object[].class },
+ },
+ new Object[] { "a", 1, 2, 3 },
+ true);
+
+ // 0-long varargs list; in case of ambiguity, the varargs component type decides:
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { String.class, Object[].class },
+ new Class[] { CharSequence.class, int[].class },
+ new Class[] { CharSequence.class, Integer[].class },
+ new Class[] { CharSequence.class, long[].class },
+ new Class[] { CharSequence.class, Long[].class },
+ new Class[] { CharSequence.class, double[].class },
+ new Class[] { CharSequence.class, Double[].class },
+ new Class[] { CharSequence.class, Serializable[].class },
+ new Class[] { CharSequence.class, Object[].class },
+ new Class[] { Object.class, int[].class },
+ },
+ new Object[] { "a" },
+ true);
+
+
+ // Different oms prefix length; in the case of ambiguity, the one with higher oms param count wins.
+ testAllCmpPermutationsInc(
+ new Class[][] {
+ new Class[] { String.class, int.class, int.class, int[].class },
+ new Class[] { String.class, int.class, int[].class },
+ new Class[] { String.class, int[].class },
+ },
+ new Object[] { "a", 1, 2, 3 },
+ true);
+ }
+
+ private void testAllCmpPermutationsInc(Class[][] sortedSignatures, Object[] args) {
+ testAllCmpPermutationsInc(sortedSignatures, args, false);
+ }
+
+ /**
+ * Compares all items with all other items in the provided descending sorted array of signatures, checking that
+ * for all valid indexes i and j, where j > i, it stands that sortedSignatures[i] > sortedSignatures[j].
+ * The comparisons are done with both operand orders, also each items is compared to itself too.
+ *
+ * @param sortedSignatures method signatures sorted by decreasing specificity
+ */
+ private void testAllCmpPermutationsInc(Class[][] sortedSignatures, Object[] args, boolean varargs) {
+ final ArgumentTypes argTs = new ArgumentTypes(args);
+ for (int i = 0; i < sortedSignatures.length; i++) {
+ for (int j = 0; j < sortedSignatures.length; j++) {
+ assertEquals("sortedSignatures[" + i + "] <==> sortedSignatures [" + j + "]",
+ _NumberUtil.getSignum(
+ Integer.valueOf(j).compareTo(i)),
+ _NumberUtil.getSignum(
+ argTs.compareParameterListPreferability(
+ sortedSignatures[i], sortedSignatures[j], varargs)));
+ }
+ }
+ }
+
+ private void testAllCmpPermutationsEqu(Class[][] signatures, Object[] args) {
+ final ArgumentTypes argTs = new ArgumentTypes(args);
+ for (int i = 0; i < signatures.length; i++) {
+ for (int j = 0; j < signatures.length; j++) {
+ assertEquals("sortedSignatures[" + i + "] <==> sortedSignatures [" + j + "]",
+ 0,
+ argTs.compareParameterListPreferability(signatures[i], signatures[j], false));
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/PrallelObjectIntrospectionTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/PrallelObjectIntrospectionTest.java b/src/test/java/org/apache/freemarker/core/model/impl/PrallelObjectIntrospectionTest.java
new file mode 100644
index 0000000..dadefba
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/PrallelObjectIntrospectionTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+public class PrallelObjectIntrospectionTest extends AbstractParallelIntrospectionTest {
+
+ public PrallelObjectIntrospectionTest(String name) {
+ super(name);
+ }
+
+ public static void main(String[] args) {
+ new PrallelObjectIntrospectionTest("almostForever")
+ .testReliability(Integer.MAX_VALUE);
+ }
+
+ @Override
+ protected TemplateHashModel getWrappedEntity(int objIdx)
+ throws TemplateModelException {
+ return (TemplateHashModel) getObjectWrapper().wrap(
+ ManyObjectsOfDifferentClasses.OBJECTS[objIdx]);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/PrallelStaticIntrospectionTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/PrallelStaticIntrospectionTest.java b/src/test/java/org/apache/freemarker/core/model/impl/PrallelStaticIntrospectionTest.java
new file mode 100644
index 0000000..0cac78a
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/PrallelStaticIntrospectionTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+public class PrallelStaticIntrospectionTest extends AbstractParallelIntrospectionTest {
+
+ private static final String STATICS_CLASS_CONTAINER_CLASS_NAME
+ = ManyStaticsOfDifferentClasses.class.getName();
+
+ public PrallelStaticIntrospectionTest(String name) {
+ super(name);
+ }
+
+ public static void main(String[] args) {
+ new PrallelStaticIntrospectionTest("almostForever")
+ .testReliability(Integer.MAX_VALUE);
+ }
+
+ @Override
+ protected TemplateHashModel getWrappedEntity(int clIdx)
+ throws TemplateModelException {
+ return (TemplateHashModel) getObjectWrapper().getStaticModels().get(
+ STATICS_CLASS_CONTAINER_CLASS_NAME + "$C"
+ + clIdx);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/RationalNumber.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/RationalNumber.java b/src/test/java/org/apache/freemarker/core/model/impl/RationalNumber.java
new file mode 100644
index 0000000..7359439
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/RationalNumber.java
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+public final class RationalNumber extends Number {
+
+ final int divident;
+ final int divisor;
+
+ public RationalNumber(int divident, int divisor) {
+ this.divident = divident;
+ this.divisor = divisor;
+ }
+
+ @Override
+ public int intValue() {
+ return divident / divisor;
+ }
+
+ @Override
+ public long longValue() {
+ return divident / (long) divisor;
+ }
+
+ @Override
+ public float floatValue() {
+ return (float) (divident / (double) divisor);
+ }
+
+ @Override
+ public double doubleValue() {
+ return divident / (double) divisor;
+ }
+
+ public int getDivident() {
+ return divident;
+ }
+
+ public int getDivisor() {
+ return divisor;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + divident;
+ result = prime * result + divisor;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RationalNumber other = (RationalNumber) obj;
+ if (divident != other.divident)
+ return false;
+ if (divisor != other.divisor)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return divident + "/" + divisor;
+ }
+
+}
\ 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/StaticModelsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
new file mode 100644
index 0000000..0287d6d
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/StaticModelsTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class StaticModelsTest {
+
+ @Test
+ public void modelCaching() throws Exception {
+ DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
+ TemplateHashModel statics = ow.getStaticModels();
+ TemplateHashModel s = (TemplateHashModel) statics.get(S.class.getName());
+ assertNotNull(s);
+ assertNotNull(s.get("F"));
+ assertNotNull(s.get("m"));
+ try {
+ s.get("x");
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), containsString("No such key"));
+ }
+
+ try {
+ statics.get("no.such.ClassExists");
+ fail();
+ } catch (TemplateModelException e) {
+ assertTrue(e.getCause() instanceof ClassNotFoundException);
+ }
+
+ TemplateModel f = s.get("F");
+ assertTrue(f instanceof TemplateScalarModel);
+ assertEquals(((TemplateScalarModel) f).getAsString(), "F OK");
+
+ TemplateModel m = s.get("m");
+ assertTrue(m instanceof TemplateMethodModelEx);
+ assertEquals(((TemplateScalarModel) ((TemplateMethodModelEx) m).exec(new ArrayList())).getAsString(), "m OK");
+
+ assertSame(s, statics.get(S.class.getName()));
+
+ ow.clearClassIntrospecitonCache();
+ TemplateHashModel sAfterClean = (TemplateHashModel) statics.get(S.class.getName());
+ assertNotSame(s, sAfterClean);
+ assertSame(sAfterClean, statics.get(S.class.getName()));
+ assertNotNull(sAfterClean.get("F"));
+ assertNotNull(sAfterClean.get("m"));
+ }
+
+ public static class S {
+
+ public static final String F = "F OK";
+
+ public static String m() {
+ return "m OK";
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/TypeFlagsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/TypeFlagsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/TypeFlagsTest.java
new file mode 100644
index 0000000..856a69f
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/TypeFlagsTest.java
@@ -0,0 +1,671 @@
+/*
+ * 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.File;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.freemarker.core.Configuration;
+
+import junit.framework.TestCase;
+
+public class TypeFlagsTest extends TestCase {
+
+ public TypeFlagsTest(String name) {
+ super(name);
+ }
+
+ private final DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
+
+ public void testSingleNumType() {
+ checkTypeFlags(SingleNumTypeC.class, "mInt",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.INTEGER | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ | TypeFlags.ACCEPTS_STRING);
+ checkTypeFlags(SingleNumTypeC.class, "mLong",
+ TypeFlags.LONG | TypeFlags.ACCEPTS_NUMBER);
+ checkTypeFlags(SingleNumTypeC.class, "mShort",
+ TypeFlags.SHORT | TypeFlags.ACCEPTS_NUMBER);
+ checkTypeFlags(SingleNumTypeC.class, "mByte",
+ TypeFlags.BYTE | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.ACCEPTS_ANY_OBJECT);
+ checkTypeFlags(SingleNumTypeC.class, "mDouble",
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER);
+ checkTypeFlags(SingleNumTypeC.class, "mFloat",
+ TypeFlags.FLOAT | TypeFlags.ACCEPTS_NUMBER);
+ checkTypeFlags(SingleNumTypeC.class, "mUnknown",
+ TypeFlags.UNKNOWN_NUMERICAL_TYPE | TypeFlags.ACCEPTS_NUMBER);
+
+ checkTypeFlags(SingleNumTypeC.class, "mVarParamCnt",
+ TypeFlags.BIG_DECIMAL | TypeFlags.ACCEPTS_NUMBER);
+ checkTypeFlags(SingleNumTypeC.class, "mVarParamCnt",
+ TypeFlags.BIG_INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER);
+ checkTypeFlags(SingleNumTypeC.class, "mVarParamCnt",
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.FLOAT | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER);
+ }
+
+ static public class SingleNumTypeC {
+ public void mInt(int a1, String a2) { }
+ public void mInt(int a1, int a2) { }
+ public void mLong(long a1) { }
+ public void mLong(Long a1) { }
+ public void mShort(short a1) { }
+ public void mByte(byte a1, boolean a2) { }
+ public void mByte(byte a1, String a2) { }
+ public void mByte(byte a1, Object a2) { }
+ public void mDouble(double a1) { }
+ public void mFloat(float a1) { }
+ public void mUnknown(RationalNumber a1) { };
+
+ public void mVarParamCnt(BigDecimal a1) { }
+ public void mVarParamCnt(BigInteger a1, Double a2) { }
+ public void mVarParamCnt(Double a1, Float a2, Integer a3) { }
+ public void mVarParamCnt(Object a1, char a2, boolean a3, File a4, Map a5, Boolean a6) { }
+ public void mVarParamCnt(Long a1, int a2, short a3, byte a4, double a5, float a6) { }
+ }
+
+ public void testMultipleNumTypes() {
+ checkTypeFlags(MultiNumTypeC.class, "m1",
+ TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
+ | TypeFlags.BYTE | TypeFlags.DOUBLE | TypeFlags.INTEGER
+ | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(MultiNumTypeC.class, "m2",
+ TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
+ | TypeFlags.SHORT | TypeFlags.LONG | TypeFlags.FLOAT
+ | TypeFlags.ACCEPTS_NUMBER | TypeFlags.CHARACTER
+ );
+
+ checkTypeFlags(MultiNumTypeC.class, "m3",
+ TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
+ | TypeFlags.BIG_DECIMAL | TypeFlags.BIG_INTEGER
+ | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.BIG_INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
+ | TypeFlags.BIG_DECIMAL | TypeFlags.UNKNOWN_NUMERICAL_TYPE
+ | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(MultiNumTypeC.class, "m4",
+ TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.FLOAT | TypeFlags.ACCEPTS_NUMBER
+ | TypeFlags.CHARACTER
+ );
+
+ checkTypeFlags(MultiNumTypeC.class, "m5",
+ TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
+ | TypeFlags.FLOAT | TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(MultiNumTypeC.class, "m6",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER
+ );
+ assertSame(getTypeFlags(MultiNumTypeC.class, "m6", false, 2), OverloadedMethodsSubset.ALL_ZEROS_ARRAY);
+ assertSame(getTypeFlags(MultiNumTypeC.class, "m6", true, 2), OverloadedMethodsSubset.ALL_ZEROS_ARRAY);
+ assertSame(getTypeFlags(MultiNumTypeC.class, "m6", false, 3), OverloadedMethodsSubset.ALL_ZEROS_ARRAY);
+ assertSame(getTypeFlags(MultiNumTypeC.class, "m6", true, 3), OverloadedMethodsSubset.ALL_ZEROS_ARRAY);
+ checkTypeFlags(MultiNumTypeC.class, "m6",
+ TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
+ 0
+ );
+ }
+
+ static public class MultiNumTypeC {
+ public void m1(byte a1) { };
+ public void m1(int a1) { };
+ public void m1(double a2) { };
+
+ public void m2(short a1) { };
+ public void m2(long a1) { };
+ public void m2(float a1) { };
+ public void m2(char a1) { };
+
+ public void m3(BigInteger a1, BigInteger a2, BigDecimal a3) { };
+ public void m3(BigDecimal a1, BigInteger a2, RationalNumber a3) { };
+
+ public void m4(float a1) { };
+ public void m4(char a1) { };
+
+ public void m5(Float a1) { };
+ public void m5(Double a1) { };
+ public void m5(Enum a1) { };
+
+ public void m6(int a1) { };
+ public void m6(File a1, Throwable a2) { };
+ public void m6(File a1, Throwable a2, StringBuilder a3) { };
+ public void m6(File a1, Throwable a2, Throwable a3) { };
+ public void m6(double a1, int a2, File a3, File a4) { };
+ public void m6(File a1, int a2, double a3, File a4) { };
+ }
+
+ public void testVarargsNums() {
+ checkTypeFlags(VarArgsC.class, "m1",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER
+ );
+ checkTypeFlags(VarArgsC.class, "m2",
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m3",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER
+ );
+ checkTypeFlags(VarArgsC.class, "m3",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.INTEGER
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+ checkTypeFlags(VarArgsC.class, "m3",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.INTEGER
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.INTEGER
+ | TypeFlags.BIG_DECIMAL | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m4",
+ TypeFlags.INTEGER | TypeFlags.LONG
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m5",
+ TypeFlags.LONG | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m6",
+ TypeFlags.LONG | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ | TypeFlags.ACCEPTS_STRING
+ );
+
+ checkTypeFlags(VarArgsC.class, "m7",
+ TypeFlags.INTEGER | TypeFlags.BYTE
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.FLOAT
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m8",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.FLOAT
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m9",
+ TypeFlags.INTEGER | TypeFlags.BYTE
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m10",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER
+ );
+ checkTypeFlags(VarArgsC.class, "m10",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.LONG | TypeFlags.DOUBLE
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m11",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.SHORT | TypeFlags.ACCEPTS_NUMBER
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+ checkTypeFlags(VarArgsC.class, "m11",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.SHORT
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.LONG | TypeFlags.DOUBLE
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m12",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER
+ );
+ checkTypeFlags(VarArgsC.class, "m12",
+ TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.SHORT
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.BYTE
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.LONG | TypeFlags.DOUBLE
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+
+ checkTypeFlags(VarArgsC.class, "m13",
+ TypeFlags.CHARACTER,
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER);
+ checkTypeFlags(VarArgsC.class, "m13",
+ TypeFlags.CHARACTER,
+ TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.UNKNOWN_NUMERICAL_TYPE
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
+ TypeFlags.DOUBLE | TypeFlags.LONG
+ | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
+ );
+ }
+
+ static public class VarArgsC {
+ public void m1(int... va) { }
+
+ public void m2(double a1, int... va) { }
+
+ public void m3(int... va) { }
+ public void m3(int a1, double... va) { }
+ public void m3(int a1, double a2, BigDecimal... va) { }
+
+ public void m4(int... va) { }
+ public void m4(long... va) { }
+
+ public void m5(Long... va) { }
+ public void m5(long... va) { }
+
+ public void m6(long... va) { }
+ public void m6(String... va) { }
+
+ public void m7(int a1, double... va) { }
+ public void m7(byte a1, float... va) { }
+
+ public void m8(int a1, double... va) { }
+ public void m8(int a1, float... va) { }
+
+ public void m9(int a1, double... va) { }
+ public void m9(byte a1, double... va) { }
+
+ public void m10(int a1, double a2, long... va) { }
+ public void m10(int a1, double... va) { }
+
+ public void m11(int a1, short a2, long... va) { }
+ public void m11(int a1, double... va) { }
+
+ public void m12(int a1, short a2, byte a3, long... va) { }
+ public void m12(int a1, double... va) { }
+
+ public void m13(char a1, double a2, RationalNumber a3, Long... va) { }
+ public void m13(char a1, Double... va) { }
+ }
+
+ public void testAllZeros() {
+ for (boolean reverse : new boolean[] { true, false }) {
+ assertSame(OverloadedMethodsSubset.ALL_ZEROS_ARRAY, getTypeFlags(AllZeroC.class, "m1", reverse, 0));
+ assertSame(OverloadedMethodsSubset.ALL_ZEROS_ARRAY, getTypeFlags(AllZeroC.class, "m2", reverse, 2));
+ assertSame(OverloadedMethodsSubset.ALL_ZEROS_ARRAY, getTypeFlags(AllZeroC.class, "m3", reverse, 1));
+ }
+ }
+
+ static public class AllZeroC {
+ public void m1() {}
+
+ public void m2(File a1, File a2) {}
+
+ public void m3(File a1) {}
+ public void m3(StringBuilder a1) {}
+ }
+
+ public void testAcceptanceNonOverloaded() {
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber1", TypeFlags.ACCEPTS_NUMBER | TypeFlags.UNKNOWN_NUMERICAL_TYPE);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber2", TypeFlags.ACCEPTS_NUMBER | TypeFlags.BYTE);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber3", TypeFlags.ACCEPTS_NUMBER | TypeFlags.BYTE);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber4", TypeFlags.ACCEPTS_NUMBER | TypeFlags.SHORT);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber5", TypeFlags.ACCEPTS_NUMBER | TypeFlags.SHORT);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber6", TypeFlags.ACCEPTS_NUMBER | TypeFlags.INTEGER);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber7", TypeFlags.ACCEPTS_NUMBER | TypeFlags.INTEGER);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber8", TypeFlags.ACCEPTS_NUMBER | TypeFlags.LONG);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber9", TypeFlags.ACCEPTS_NUMBER | TypeFlags.LONG);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber10", TypeFlags.ACCEPTS_NUMBER | TypeFlags.FLOAT);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber11", TypeFlags.ACCEPTS_NUMBER | TypeFlags.FLOAT);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber12", TypeFlags.ACCEPTS_NUMBER | TypeFlags.DOUBLE);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber13", TypeFlags.ACCEPTS_NUMBER | TypeFlags.DOUBLE);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber14", TypeFlags.ACCEPTS_NUMBER | TypeFlags.BIG_INTEGER);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber15", TypeFlags.ACCEPTS_NUMBER | TypeFlags.BIG_DECIMAL);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber16", TypeFlags.ACCEPTS_NUMBER | TypeFlags.UNKNOWN_NUMERICAL_TYPE);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mDate", TypeFlags.ACCEPTS_DATE);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mSQLDate1", 0);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mSQLDate2", 0);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mString", TypeFlags.ACCEPTS_STRING);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mCharSequence", TypeFlags.ACCEPTS_STRING);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mStringBuilder", 0);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mBool", TypeFlags.ACCEPTS_BOOLEAN);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mBoolean", TypeFlags.ACCEPTS_BOOLEAN);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mMap1", TypeFlags.ACCEPTS_MAP);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mMap2", 0);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mList1", TypeFlags.ACCEPTS_LIST);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mList2", 0);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mSet1", TypeFlags.ACCEPTS_SET);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mSet2", 0);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mCollection", TypeFlags.ACCEPTS_SET | TypeFlags.ACCEPTS_LIST);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mChar1", TypeFlags.CHARACTER);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mChar2", TypeFlags.CHARACTER);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mArray1", TypeFlags.ACCEPTS_ARRAY);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mArray2", TypeFlags.ACCEPTS_ARRAY);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mArray3", TypeFlags.ACCEPTS_ARRAY);
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mArray4", TypeFlags.ACCEPTS_ARRAY);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mObject", TypeFlags.ACCEPTS_ANY_OBJECT);
+ assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_NUMBER) != 0);
+ assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_STRING) != 0);
+ assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_BOOLEAN) != 0);
+ assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_MAP) != 0);
+ assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_LIST) != 0);
+ assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_SET) != 0);
+ assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_ARRAY) != 0);
+ assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.CHARACTER) == 0); // deliberatly 0
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mMapDate", 0);
+
+ checkTypeFlags(AcceptanceNonoverloadedC.class, "mDateBooleanList",
+ TypeFlags.ACCEPTS_DATE, TypeFlags.ACCEPTS_BOOLEAN, TypeFlags.ACCEPTS_LIST);
+ }
+
+ static public class AcceptanceNonoverloadedC {
+ public void mNumber1(Number a1) {}
+ public void mNumber2(Byte a1) {}
+ public void mNumber3(byte a1) {}
+ public void mNumber4(Short a1) {}
+ public void mNumber5(short a1) {}
+ public void mNumber6(Integer a1) {}
+ public void mNumber7(int a1) {}
+ public void mNumber8(Long a1) {}
+ public void mNumber9(long a1) {}
+ public void mNumber10(Float a1) {}
+ public void mNumber11(float a1) {}
+ public void mNumber12(Double a1) {}
+ public void mNumber13(double a1) {}
+ public void mNumber14(BigInteger a1) {}
+ public void mNumber15(BigDecimal a1) {}
+ public void mNumber16(RationalNumber a1) {}
+
+ public void mDate(Date a1) {}
+ public void mSQLDate1(java.sql.Date a1) {}
+ public void mSQLDate2(java.sql.Timestamp a1) {}
+
+ public void mString(String a1) {}
+ public void mCharSequence(CharSequence a1) {}
+ public void mStringBuilder(StringBuilder a1) {}
+
+ public void mBool(boolean a1) {}
+ public void mBoolean(Boolean a1) {}
+
+ public void mMap1(Map a1) {}
+ public void mMap2(LinkedHashMap a1) {}
+
+ public void mList1(List a1) {}
+ public void mList2(ArrayList a1) {}
+
+ public void mSet1(Set a1) {}
+ public void mSet2(HashSet a1) {}
+
+ public void mCollection(Collection a1) {}
+
+ public void mMapDate(MapDate a1) {}
+
+ public void mChar1(Character a1) {}
+ public void mChar2(char a1) {}
+
+ public void mArray1(Object[] a1) {}
+ public void mArray2(int[] a1) {}
+ public void mArray3(Integer[] a1) {}
+ public void mArray4(Void[] a1) {}
+
+ public void mObject(Object a1) {}
+
+ public void mDateBooleanList(Date a1, boolean a2, List a3) {}
+ }
+
+ public void testAcceptanceOverloaded() {
+ checkTypeFlags(AcceptanceOverloadedC.class, "mLongDateList",
+ TypeFlags.ACCEPTS_NUMBER | TypeFlags.LONG | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
+ | TypeFlags.ACCEPTS_DATE | TypeFlags.ACCEPTS_LIST);
+ checkTypeFlags(AcceptanceOverloadedC.class, "mBoolean", TypeFlags.ACCEPTS_BOOLEAN);
+ checkTypeFlags(AcceptanceOverloadedC.class, "mStringChar",
+ TypeFlags.ACCEPTS_STRING | TypeFlags.CHARACTER);
+ checkTypeFlags(AcceptanceOverloadedC.class, "mStringFile", TypeFlags.ACCEPTS_STRING);
+ checkTypeFlags(AcceptanceOverloadedC.class, "mMapObject", TypeFlags.ACCEPTS_ANY_OBJECT);
+ checkTypeFlags(AcceptanceOverloadedC.class, "mSetMap", TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_SET);
+ checkTypeFlags(AcceptanceOverloadedC.class, "mCollectionMap",
+ TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_SET | TypeFlags.ACCEPTS_LIST);
+ checkTypeFlags(AcceptanceOverloadedC.class, "mArray", TypeFlags.ACCEPTS_ARRAY);
+ checkTypeFlags(AcceptanceOverloadedC.class, "mArrayList", TypeFlags.ACCEPTS_ARRAY | TypeFlags.ACCEPTS_LIST);
+
+ checkTypeFlags(AcceptanceOverloadedC.class, "mStringCollectionThenBooleanThenMapList",
+ TypeFlags.ACCEPTS_STRING | TypeFlags.ACCEPTS_LIST | TypeFlags.ACCEPTS_SET,
+ TypeFlags.ACCEPTS_BOOLEAN,
+ TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_LIST);
+ }
+
+ static public class AcceptanceOverloadedC {
+ public void mLongDateList(long a1) {}
+ public void mLongDateList(Date a1) {}
+ public void mLongDateList(List a1) {}
+
+ public void mBoolean(boolean a1) {}
+ public void mBoolean(Boolean a1) {}
+
+ public void mDate(Date a1) {}
+ public void mDate(java.sql.Date a1) {}
+ public void mDate(java.sql.Timestamp a1) {}
+
+ public void mStringChar(String a1) {}
+ public void mStringChar(char a1) {}
+ public void mStringChar(Character a1) {}
+
+ public void mStringFile(String a1) {}
+ public void mStringFile(File a1) {}
+
+ public void mMapObject(Map a1) {}
+ public void mMapObject(Object a1) {}
+
+ public void mSetMap(Set a1) {}
+ public void mSetMap(Map a1) {}
+
+ public void mCollectionMap(Collection a1) {}
+ public void mCollectionMap(Map a1) {}
+
+ public void mArray(Object[] a1) {}
+ public void mArray(int[] a1) {}
+ public void mArray(Integer[] a1) {}
+ public void mArray(Void[] a1) {}
+
+ public void mArrayList(String[] a1) {}
+ public void mArrayList(List a1) {}
+
+ public void mStringCollectionThenBooleanThenMapList(String a1, boolean a2, Map a3) {}
+ public void mStringCollectionThenBooleanThenMapList(Collection a1, boolean a2, Map a3) {}
+ public void mStringCollectionThenBooleanThenMapList(String a1, boolean a2, List a3) {}
+ }
+
+ public void testAcceptanceVarargsC() {
+ checkTypeFlags(AcceptanceVarArgsC.class, "m1",
+ TypeFlags.ACCEPTS_LIST | TypeFlags.ACCEPTS_STRING);
+
+ checkTypeFlags(AcceptanceVarArgsC.class, "m2",
+ TypeFlags.ACCEPTS_MAP,
+ TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_STRING | TypeFlags.ACCEPTS_BOOLEAN,
+ TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_STRING);
+ checkTypeFlags(AcceptanceVarArgsC.class, "m2",
+ TypeFlags.ACCEPTS_MAP,
+ TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_STRING | TypeFlags.ACCEPTS_BOOLEAN);
+ checkTypeFlags(AcceptanceVarArgsC.class, "m2",
+ TypeFlags.ACCEPTS_MAP);
+
+ checkTypeFlags(AcceptanceVarArgsC.class, "m3",
+ TypeFlags.ACCEPTS_BOOLEAN);
+ checkTypeFlags(AcceptanceVarArgsC.class, "m3",
+ TypeFlags.ACCEPTS_BOOLEAN | TypeFlags.ACCEPTS_STRING,
+ TypeFlags.ACCEPTS_BOOLEAN | TypeFlags.ACCEPTS_MAP,
+ TypeFlags.ACCEPTS_BOOLEAN | TypeFlags.CHARACTER,
+ TypeFlags.ACCEPTS_BOOLEAN);
+ }
+
+ static public class AcceptanceVarArgsC {
+ public void m1(List... a1) {}
+ public void m1(String... a1) {}
+
+ public void m2(Map a1, String... a2) {}
+ public void m2(Map a1, boolean a2, String... a3) {}
+ public void m2(Map... a1) {}
+
+ public void m3(String a1, Map a2, char a3, Boolean... a4) {}
+ public void m3(boolean... a1) {}
+ }
+
+ static public class MapDate extends Date implements Map {
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return false;
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return false;
+ }
+
+ @Override
+ public Object get(Object key) {
+ return null;
+ }
+
+ @Override
+ public Object put(Object key, Object value) {
+ return null;
+ }
+
+ @Override
+ public Object remove(Object key) {
+ return null;
+ }
+
+ @Override
+ public void putAll(Map m) {
+ }
+
+ @Override
+ public void clear() {
+ }
+
+ @Override
+ public Set keySet() {
+ return null;
+ }
+
+ @Override
+ public Collection values() {
+ return null;
+ }
+
+ @Override
+ public Set entrySet() {
+ return null;
+ }
+
+ }
+
+ private OverloadedMethodsSubset newOverloadedMethodsSubset(Class cl, String methodName, final boolean desc) {
+ final Method[] ms = cl.getMethods();
+
+ final List<Method> filteredMethods = new ArrayList();
+ for (Method m : ms) {
+ if (m.getName().equals(methodName)) {
+ filteredMethods.add(m);
+ }
+ }
+ // As the order in which getMethods() returns the methods is undefined, we sort them for test predictability:
+ Collections.sort(filteredMethods, new Comparator<Method>() {
+ @Override
+ public int compare(Method o1, Method o2) {
+ int res = o1.toString().compareTo(o2.toString());
+ return desc ? -res : res;
+ }
+ });
+
+ final OverloadedMethodsSubset oms = cl.getName().indexOf("VarArgs") == -1
+ ? new OverloadedFixArgsMethods() : new OverloadedVarArgsMethods();
+ for (Method m : filteredMethods) {
+ oms.addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(m, m.getParameterTypes()));
+ }
+ return oms;
+ }
+
+ private void checkTypeFlags(Class cl, String methodName, int... expectedTypeFlags) {
+ checkTypeFlags(cl, methodName, false, expectedTypeFlags);
+ checkTypeFlags(cl, methodName, true, expectedTypeFlags);
+ }
+
+ private void checkTypeFlags(Class cl, String methodName, boolean revMetOrder, int... expectedTypeFlags) {
+ int[] actualParamTypes = getTypeFlags(cl, methodName, revMetOrder, expectedTypeFlags.length);
+ assertNotNull("Method " + methodName + "(#" + expectedTypeFlags.length + ") doesn't exist", actualParamTypes);
+ if (actualParamTypes != OverloadedMethodsSubset.ALL_ZEROS_ARRAY) {
+ assertEquals(expectedTypeFlags.length, actualParamTypes.length);
+ for (int i = 0; i < expectedTypeFlags.length; i++) {
+ assertEquals(expectedTypeFlags[i], actualParamTypes[i]);
+ }
+ } else {
+ for (int expectedTypeFlag : expectedTypeFlags) {
+ assertEquals(expectedTypeFlag, 0);
+ }
+ }
+ }
+
+ private int[] getTypeFlags(Class cl, String methodName, boolean revMetOrder, int paramCnt) {
+ OverloadedMethodsSubset oms = newOverloadedMethodsSubset(cl, methodName, revMetOrder);
+ int[] actualParamTypes = oms.getTypeFlags(paramCnt);
+ return actualParamTypes;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/AbstractParallelIntrospectionTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/AbstractParallelIntrospectionTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/AbstractParallelIntrospectionTest.java
deleted file mode 100644
index 62c4251..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/AbstractParallelIntrospectionTest.java
+++ /dev/null
@@ -1,125 +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 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 BeansWrapper beansWrapper = new BeansWrapper(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 BeansWrapper getBeansWrapper() {
- return beansWrapper;
- }
-
- 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) {
- beansWrapper.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/beans/AlphabeticalMethodSorter.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/AlphabeticalMethodSorter.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/AlphabeticalMethodSorter.java
deleted file mode 100644
index 57611f1..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/AlphabeticalMethodSorter.java
+++ /dev/null
@@ -1,49 +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.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/beans/BeansAPINewInstanceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansAPINewInstanceTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansAPINewInstanceTest.java
deleted file mode 100644
index 34e2a4f..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansAPINewInstanceTest.java
+++ /dev/null
@@ -1,134 +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 org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.test.util.TestUtil;
-
-import junit.framework.TestCase;
-
-public class BeansAPINewInstanceTest extends TestCase {
-
- private BeansWrapper beansWrapper = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
-
- public BeansAPINewInstanceTest(String name) {
- super(name);
- }
-
- @SuppressWarnings("boxing")
- public void test() throws Exception {
- testClassConstructors("");
- testClassConstructors("int... [1]", 1);
- testClassConstructors("int 1, int 2", 1, 2);
- testClassConstructors("int... [1, 2, 3]", 1, 2, 3);
- testClassConstructors("int... [1, 2, 3]", 1, (byte) 2, (short) 3);
- try {
- testClassConstructors("int... [1, 2, 3]", 1, 2, (long) 3);
- fail();
- } catch (NoSuchMethodException e) {
- // Expected
- }
-
- testClassConstructors("int 1, int 2", (byte) 1, (short) 2);
- testClassConstructors("double 1.0, double 2.0", (long) 1, (short) 2);
- testClassConstructors("double 1.0, double 2.0", 1, 2f);
- testClassConstructors("Integer null, Integer null", null, null);
- testClassConstructors("Integer null, Integer 1", null, 1);
- testClassConstructors("int 1, String s", 1, "s");
- testClassConstructors("int 1, String null", 1, null);
- testClassConstructors("Object null, Object s", null, "s");
- testClassConstructors("Object 1.0, Object s", 1f, "s");
-
- testClassConstructors("Object s, int... [1, 2]", "s", 1, 2);
- testClassConstructors("Object s, int... []", "s");
-
- testClassConstructors2("int 1, int 2", (byte) 1, (short) 2);
- try {
- testClassConstructors2("int 1, int 2", 1, 2L);
- fail();
- } catch (NoSuchMethodException e) {
- // Expected
- }
- try {
- testClassConstructors2("", "", "");
- fail();
- } catch (NoSuchMethodException e) {
- // Expected
- }
- try {
- testClassConstructors2("int 1", 1);
- fail();
- } catch (NoSuchMethodException e) {
- // Expected
- }
- try {
- testClassConstructors2("");
- fail();
- } catch (NoSuchMethodException e) {
- // Expected
- }
- }
-
- private void testClassConstructors(String expected, Object... args) throws Exception {
- testCall(expected, Constructors.class, args);
- }
-
- private void testClassConstructors2(String expected, Object... args) throws Exception {
- testCall(expected, Constructors2.class, args);
- }
-
- private void testCall(String expected, Class cl, Object... args) throws Exception {
- Object obj = _BeansAPI.newInstance(cl, args, beansWrapper);
- assertEquals(expected, obj.toString());
- }
-
- public static class Constructors {
- private final String s;
-
- public Constructors() { s = ""; }
-
- public Constructors(int x, int y) { s = "int " + x + ", int " + y; }
- public Constructors(int x, String y) { s = "int " + x + ", String " + y; }
- public Constructors(int x, long y) { s = "int " + x + ", long " + y; }
- public Constructors(double x, double y) { s = "double " + x + ", double " + y; }
- public Constructors(Integer x, Integer y) { s = "Integer " + x + ", Integer " + y; }
- public Constructors(Object x, Object y) { s = "Object " + x + ", Object " + y; }
-
- public Constructors(int... xs) { s = "int... " + TestUtil.arrayToString(xs); }
- public Constructors(Object x, int... ys) { s = "Object " + x + ", int... " + TestUtil.arrayToString(ys); }
-
- @Override
- public String toString() {
- return s;
- }
- }
-
- public static class Constructors2 {
- private final String s;
-
- public Constructors2(int x, int y) { s = "int " + x + ", int " + y; }
-
- @Override
- public String toString() {
- return s;
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBasics.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBasics.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBasics.java
deleted file mode 100644
index bc6b8d7..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBasics.java
+++ /dev/null
@@ -1,62 +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 static org.junit.Assert.*;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class BeansWrapperBasics {
-
- @SuppressWarnings("boxing")
- @Test
- public void testAPIBuiltInSupport() throws TemplateModelException {
- {
- BeansWrapper bw = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
- assertWrappingResult(StringModel.class, bw.wrap("s"));
- assertWrappingResult(NumberModel.class, bw.wrap(1.5));
- assertWrappingResult(BooleanModel.class, bw.wrap(true));
- assertWrappingResult(CollectionModel.class, bw.wrap(ImmutableList.of(1)));
- assertWrappingResult(MapModel.class, bw.wrap(ImmutableMap.of("a", 1)));
- }
-
- {
- BeansWrapperBuilder bwb = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- bwb.setSimpleMapWrapper(true);
- BeansWrapper bw = bwb.build();
- assertWrappingResult(SimpleMapModel.class, bw.wrap(ImmutableMap.of("a", 1)));
- }
- }
-
- private void assertWrappingResult(Class<? extends TemplateModel> expectedClass, TemplateModel tm)
- throws TemplateModelException {
- assertTrue(expectedClass.isInstance(tm));
- // All BeansWrapper products support `?api`:
- assertTrue(((TemplateModelWithAPISupport) tm).getAPI() instanceof APIModel);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperCachesTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperCachesTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperCachesTest.java
deleted file mode 100644
index 8221a36..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperCachesTest.java
+++ /dev/null
@@ -1,99 +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 static org.junit.Assert.*;
-
-import java.lang.ref.Reference;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecision;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecisionInput;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class BeansWrapperCachesTest {
-
- @Test
- public void introspectionSettingChanges() {
- BeansWrapper bw = new BeansWrapper(Configuration.VERSION_3_0_0);
- ClassIntrospector ci1 = bw.getClassIntrospector();
- checkRegisteredModelFactories(ci1, bw.getStaticModels(), bw.getEnumModels(), bw.getModelCache());
-
- bw.setExposeFields(true);
- ClassIntrospector ci2 = bw.getClassIntrospector();
- assertNotSame(ci1, ci2);
- checkRegisteredModelFactories(ci1);
- checkRegisteredModelFactories(ci2, bw.getStaticModels(), bw.getEnumModels(), bw.getModelCache());
-
- bw.setExposureLevel(BeansWrapper.EXPOSE_ALL);
- ClassIntrospector ci3 = bw.getClassIntrospector();
- assertNotSame(ci2, ci3);
- checkRegisteredModelFactories(ci2);
- checkRegisteredModelFactories(ci3, bw.getStaticModels(), bw.getEnumModels(), bw.getModelCache());
-
- MethodAppearanceFineTuner maf = new MethodAppearanceFineTuner() {
- @Override
- public void process(MethodAppearanceDecisionInput in, MethodAppearanceDecision out) {
- // nop
- }
- };
- bw.setMethodAppearanceFineTuner(maf);
- ClassIntrospector ci4 = bw.getClassIntrospector();
- assertNotSame(ci3, ci4);
- checkRegisteredModelFactories(ci3);
- checkRegisteredModelFactories(ci4, bw.getStaticModels(), bw.getEnumModels(), bw.getModelCache());
-
- bw.setExposeFields(true);
- assertSame(ci4, bw.getClassIntrospector());
- bw.setExposureLevel(BeansWrapper.EXPOSE_ALL);
- assertSame(ci4, bw.getClassIntrospector());
- bw.setMethodAppearanceFineTuner(maf);
- assertSame(ci4, bw.getClassIntrospector());
- checkRegisteredModelFactories(ci4, bw.getStaticModels(), bw.getEnumModels(), bw.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("Actaul 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/beans/BeansWrapperDesc.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperDesc.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperDesc.java
deleted file mode 100644
index 6fd9a8c..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperDesc.java
+++ /dev/null
@@ -1,30 +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 org.apache.freemarker.core.Configuration;
-
-public class BeansWrapperDesc extends BeansWrapperWithShortedMethods {
-
- public BeansWrapperDesc() {
- 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/beans/BeansWrapperInc.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperInc.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperInc.java
deleted file mode 100644
index b347b95..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperInc.java
+++ /dev/null
@@ -1,30 +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 org.apache.freemarker.core.Configuration;
-
-public class BeansWrapperInc extends BeansWrapperWithShortedMethods {
-
- public BeansWrapperInc() {
- super(Configuration.VERSION_3_0_0, false);
- }
-
-}
[19/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java b/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
new file mode 100644
index 0000000..9c483d6
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
@@ -0,0 +1,362 @@
+/*
+ * 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.IndexedPropertyDescriptor;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.freemarker.core._CoreLogs;
+import org.apache.freemarker.core._DelayedFTLTypeDescription;
+import org.apache.freemarker.core._DelayedJQuote;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.AdapterTemplateModel;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.model.WrapperTemplateModel;
+import org.apache.freemarker.core.util._StringUtil;
+import org.slf4j.Logger;
+
+/**
+ * A class that will wrap an arbitrary object into {@link org.apache.freemarker.core.model.TemplateHashModel}
+ * interface allowing calls to arbitrary property getters and invocation of
+ * accessible methods on the object from a template using the
+ * <tt>object.foo</tt> to access properties and <tt>object.bar(arg1, arg2)</tt> to
+ * invoke methods on it. You can also use the <tt>object.foo[index]</tt> syntax to
+ * access indexed properties. It uses Beans {@link java.beans.Introspector}
+ * to dynamically discover the properties and methods.
+ */
+
+public class BeanModel
+implements
+ TemplateHashModelEx, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport {
+
+ private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER;
+
+ protected final Object object;
+ protected final DefaultObjectWrapper wrapper;
+
+ // We use this to represent an unknown value as opposed to known value of null (JR)
+ static final TemplateModel UNKNOWN = new SimpleScalar("UNKNOWN");
+
+ static final ModelFactory FACTORY =
+ new ModelFactory()
+ {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new BeanModel(object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+ // I've tried to use a volatile ConcurrentHashMap field instead of HashMap + synchronized(this), but oddly it was
+ // a bit slower, at least on Java 8 u66.
+ private HashMap<Object, TemplateModel> memberCache;
+
+ /**
+ * Creates a new model that wraps the specified object. Note that there are
+ * specialized subclasses of this class for wrapping arrays, collections,
+ * enumeration, iterators, and maps. Note also that the superclass can be
+ * used to wrap String objects if only scalar functionality is needed. You
+ * can also choose to delegate the choice over which model class is used for
+ * wrapping to {@link DefaultObjectWrapper#wrap(Object)}.
+ * @param object the object to wrap into a model.
+ * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+ * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+ * model gains many attributes from its wrapper, including the caching
+ * behavior, method exposure level, method-over-item shadowing policy etc.
+ */
+ public BeanModel(Object object, DefaultObjectWrapper wrapper) {
+ // [2.4]: All models were introspected here, then the results was discareded, and get() will just do the
+ // introspection again. So is this necessary? (The inrospectNow parameter was added in 2.3.21 to allow
+ // lazy-introspecting DefaultObjectWrapper.trueModel|falseModel.)
+ this(object, wrapper, true);
+ }
+
+ /** @since 2.3.21 */
+ BeanModel(Object object, DefaultObjectWrapper wrapper, boolean inrospectNow) {
+ this.object = object;
+ this.wrapper = wrapper;
+ if (inrospectNow && object != null) {
+ // [2.4]: Could this be removed?
+ wrapper.getClassIntrospector().get(object.getClass());
+ }
+ }
+
+ /**
+ * Uses Beans introspection to locate a property or method with name
+ * matching the key name. If a method or property is found, it's wrapped
+ * into {@link org.apache.freemarker.core.model.TemplateMethodModelEx} (for a method or
+ * indexed property), or evaluated on-the-fly and the return value wrapped
+ * into appropriate model (for a simple property) Models for various
+ * properties and methods are cached on a per-class basis, so the costly
+ * introspection is performed only once per property or method of a class.
+ * (Side-note: this also implies that any class whose method has been called
+ * will be strongly referred to by the framework and will not become
+ * unloadable until this class has been unloaded first. Normally this is not
+ * an issue, but can be in a rare scenario where you create many classes on-
+ * the-fly. Also, as the cache grows with new classes and methods introduced
+ * to the framework, it may appear as if it were leaking memory. The
+ * framework does, however detect class reloads (if you happen to be in an
+ * environment that does this kind of things--servlet containers do it when
+ * they reload a web application) and flushes the cache. If no method or
+ * property matching the key is found, the framework will try to invoke
+ * methods with signature
+ * <tt>non-void-return-type get(java.lang.String)</tt>,
+ * then <tt>non-void-return-type get(java.lang.Object)</tt>, or
+ * alternatively (if the wrapped object is a resource bundle)
+ * <tt>Object getObject(java.lang.String)</tt>.
+ * @throws TemplateModelException if there was no property nor method nor
+ * a generic <tt>get</tt> method to invoke.
+ */
+ @Override
+ public TemplateModel get(String key)
+ throws TemplateModelException {
+ Class<?> clazz = object.getClass();
+ Map<Object, Object> classInfo = wrapper.getClassIntrospector().get(clazz);
+ TemplateModel retval = null;
+
+ try {
+ if (wrapper.isMethodsShadowItems()) {
+ Object fd = classInfo.get(key);
+ if (fd != null) {
+ retval = invokeThroughDescriptor(fd, classInfo);
+ } else {
+ retval = invokeGenericGet(classInfo, clazz, key);
+ }
+ } else {
+ TemplateModel model = invokeGenericGet(classInfo, clazz, key);
+ final TemplateModel nullModel = wrapper.wrap(null);
+ if (model != nullModel && model != UNKNOWN) {
+ return model;
+ }
+ Object fd = classInfo.get(key);
+ if (fd != null) {
+ retval = invokeThroughDescriptor(fd, classInfo);
+ if (retval == UNKNOWN && model == nullModel) {
+ // This is the (somewhat subtle) case where the generic get() returns null
+ // and we have no bean info, so we respect the fact that
+ // the generic get() returns null and return null. (JR)
+ retval = nullModel;
+ }
+ }
+ }
+ if (retval == UNKNOWN) {
+ if (wrapper.isStrict()) {
+ throw new InvalidPropertyException("No such bean property: " + key);
+ } else {
+ logNoSuchKey(key, classInfo);
+ }
+ retval = wrapper.wrap(null);
+ }
+ return retval;
+ } catch (TemplateModelException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new _TemplateModelException(e,
+ "An error has occurred when reading existing sub-variable ", new _DelayedJQuote(key),
+ "; see cause exception! The type of the containing value was: ",
+ new _DelayedFTLTypeDescription(this)
+ );
+ }
+ }
+
+ private void logNoSuchKey(String key, Map<?, ?> keyMap) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Key " + _StringUtil.jQuoteNoXSS(key) + " was not found on instance of " +
+ object.getClass().getName() + ". Introspection information for " +
+ "the class is: " + keyMap);
+ }
+ }
+
+ /**
+ * Whether the model has a plain get(String) or get(Object) method
+ */
+
+ protected boolean hasPlainGetMethod() {
+ return wrapper.getClassIntrospector().get(object.getClass()).get(ClassIntrospector.GENERIC_GET_KEY) != null;
+ }
+
+ private TemplateModel invokeThroughDescriptor(Object desc, Map<Object, Object> classInfo)
+ throws IllegalAccessException, InvocationTargetException, TemplateModelException {
+ // See if this particular instance has a cached implementation for the requested feature descriptor
+ TemplateModel cachedModel;
+ synchronized (this) {
+ cachedModel = memberCache != null ? memberCache.get(desc) : null;
+ }
+
+ if (cachedModel != null) {
+ return cachedModel;
+ }
+
+ TemplateModel resultModel = UNKNOWN;
+ if (desc instanceof IndexedPropertyDescriptor) {
+ Method readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod();
+ resultModel = cachedModel =
+ new SimpleMethodModel(object, readMethod,
+ ClassIntrospector.getArgTypes(classInfo, readMethod), wrapper);
+ } else if (desc instanceof PropertyDescriptor) {
+ PropertyDescriptor pd = (PropertyDescriptor) desc;
+ resultModel = wrapper.invokeMethod(object, pd.getReadMethod(), null);
+ // cachedModel remains null, as we don't cache these
+ } else if (desc instanceof Field) {
+ resultModel = wrapper.wrap(((Field) desc).get(object));
+ // cachedModel remains null, as we don't cache these
+ } else if (desc instanceof Method) {
+ Method method = (Method) desc;
+ resultModel = cachedModel = new SimpleMethodModel(
+ object, method, ClassIntrospector.getArgTypes(classInfo, method), wrapper);
+ } else if (desc instanceof OverloadedMethods) {
+ resultModel = cachedModel = new OverloadedMethodsModel(
+ object, (OverloadedMethods) desc, wrapper);
+ }
+
+ // If new cachedModel was created, cache it
+ if (cachedModel != null) {
+ synchronized (this) {
+ if (memberCache == null) {
+ memberCache = new HashMap<>();
+ }
+ memberCache.put(desc, cachedModel);
+ }
+ }
+ return resultModel;
+ }
+
+ void clearMemberCache() {
+ synchronized (this) {
+ memberCache = null;
+ }
+ }
+
+ protected TemplateModel invokeGenericGet(Map/*<Object, Object>*/ classInfo, Class<?> clazz, String key)
+ throws IllegalAccessException, InvocationTargetException,
+ TemplateModelException {
+ Method genericGet = (Method) classInfo.get(ClassIntrospector.GENERIC_GET_KEY);
+ if (genericGet == null) {
+ return UNKNOWN;
+ }
+
+ return wrapper.invokeMethod(object, genericGet, new Object[] { key });
+ }
+
+ protected TemplateModel wrap(Object obj)
+ throws TemplateModelException {
+ return wrapper.getOuterIdentity().wrap(obj);
+ }
+
+ protected Object unwrap(TemplateModel model)
+ throws TemplateModelException {
+ return wrapper.unwrap(model);
+ }
+
+ /**
+ * Tells whether the model is considered to be empty.
+ * It is empty if the wrapped object is a 0 length {@link String}, or an empty {@link Collection} or and empty
+ * {@link Map}, or an {@link Iterator} that has no more items, or a {@link Boolean#FALSE}, or {@code null}.
+ */
+ @Override
+ public boolean isEmpty() {
+ if (object instanceof String) {
+ return ((String) object).length() == 0;
+ }
+ if (object instanceof Collection) {
+ return ((Collection<?>) object).isEmpty();
+ }
+ if (object instanceof Iterator) {
+ return !((Iterator<?>) object).hasNext();
+ }
+ if (object instanceof Map) {
+ return ((Map<?,?>) object).isEmpty();
+ }
+ // [FM3] Why's FALSE empty?
+ return object == null || Boolean.FALSE.equals(object);
+ }
+
+ /**
+ * Returns the same as {@link #getWrappedObject()}; to ensure that, this method will be final starting from 2.4.
+ * This behavior of {@link BeanModel} is assumed by some FreeMarker code.
+ */
+ @Override
+ public Object getAdaptedObject(Class<?> hint) {
+ return object; // return getWrappedObject(); starting from 2.4
+ }
+
+ @Override
+ public Object getWrappedObject() {
+ return object;
+ }
+
+ @Override
+ public int size() {
+ return wrapper.getClassIntrospector().keyCount(object.getClass());
+ }
+
+ @Override
+ public TemplateCollectionModel keys() {
+ return new CollectionAndSequence(new SimpleSequence(keySet(), wrapper));
+ }
+
+ @Override
+ public TemplateCollectionModel values() throws TemplateModelException {
+ List<Object> values = new ArrayList<>(size());
+ TemplateModelIterator it = keys().iterator();
+ while (it.hasNext()) {
+ String key = ((TemplateScalarModel) it.next()).getAsString();
+ values.add(get(key));
+ }
+ return new CollectionAndSequence(new SimpleSequence(values, wrapper));
+ }
+
+ @Override
+ public String toString() {
+ return object.toString();
+ }
+
+ /**
+ * Helper method to support TemplateHashModelEx. Returns the Set of
+ * Strings which are available via the TemplateHashModel
+ * interface. Subclasses that override <tt>invokeGenericGet</tt> to
+ * provide additional hash keys should also override this method.
+ */
+ protected Set/*<Object>*/ keySet() {
+ return wrapper.getClassIntrospector().keySet(object.getClass());
+ }
+
+ @Override
+ public TemplateModel getAPI() throws TemplateModelException {
+ return wrapper.wrapAsAPI(object);
+ }
+
+}
\ 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/BeansModelCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/BeansModelCache.java b/src/main/java/org/apache/freemarker/core/model/impl/BeansModelCache.java
new file mode 100644
index 0000000..7808d48
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/BeansModelCache.java
@@ -0,0 +1,73 @@
+/*
+ * 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.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.freemarker.core.model.TemplateModel;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+public class BeansModelCache extends ModelCache {
+ private final Map classToFactory = new ConcurrentHashMap();
+ private final Set mappedClassNames = new HashSet();
+
+ private final DefaultObjectWrapper wrapper;
+
+ BeansModelCache(DefaultObjectWrapper wrapper) {
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ protected boolean isCacheable(Object object) {
+ return object.getClass() != Boolean.class;
+ }
+
+ @Override
+ @SuppressFBWarnings(value="JLM_JSR166_UTILCONCURRENT_MONITORENTER", justification="Locks for factory creation only")
+ protected TemplateModel create(Object object) {
+ Class clazz = object.getClass();
+
+ ModelFactory factory = (ModelFactory) classToFactory.get(clazz);
+
+ if (factory == null) {
+ // Synchronized so that we won't unnecessarily create the same factory for multiple times in parallel.
+ synchronized (classToFactory) {
+ factory = (ModelFactory) classToFactory.get(clazz);
+ if (factory == null) {
+ String className = clazz.getName();
+ // clear mappings when class reloading is detected
+ if (!mappedClassNames.add(className)) {
+ classToFactory.clear();
+ mappedClassNames.clear();
+ mappedClassNames.add(className);
+ }
+ factory = wrapper.getModelFactory(clazz);
+ classToFactory.put(clazz, factory);
+ }
+ }
+ }
+
+ return factory.create(object, wrapper);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/BooleanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/BooleanModel.java b/src/main/java/org/apache/freemarker/core/model/impl/BooleanModel.java
new file mode 100644
index 0000000..9db421f
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/BooleanModel.java
@@ -0,0 +1,40 @@
+/*
+ * 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.model.TemplateBooleanModel;
+
+/**
+ * <p>A class that will wrap instances of {@link java.lang.Boolean} into a
+ * {@link TemplateBooleanModel}.
+ */
+public class BooleanModel extends BeanModel implements TemplateBooleanModel {
+ private final boolean value;
+
+ public BooleanModel(Boolean bool, DefaultObjectWrapper wrapper) {
+ super(bool, wrapper, false);
+ value = bool.booleanValue();
+ }
+
+ @Override
+ public boolean getAsBoolean() {
+ return value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/CallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/CallableMemberDescriptor.java b/src/main/java/org/apache/freemarker/core/model/impl/CallableMemberDescriptor.java
new file mode 100644
index 0000000..5548796
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/CallableMemberDescriptor.java
@@ -0,0 +1,56 @@
+/*
+ * 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.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * Packs a {@link Method} or {@link Constructor} together with its parameter types. The actual
+ * {@link Method} or {@link Constructor} is not exposed by the API, because in rare cases calling them require
+ * type conversion that the Java reflection API can't do, hence the developer shouldn't be tempted to call them
+ * directly.
+ */
+abstract class CallableMemberDescriptor extends MaybeEmptyCallableMemberDescriptor {
+
+ abstract TemplateModel invokeMethod(DefaultObjectWrapper ow, Object obj, Object[] args)
+ throws TemplateModelException, InvocationTargetException, IllegalAccessException;
+
+ abstract Object invokeConstructor(DefaultObjectWrapper ow, Object[] args)
+ throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException,
+ TemplateModelException;
+
+ abstract String getDeclaration();
+
+ abstract boolean isConstructor();
+
+ abstract boolean isStatic();
+
+ abstract boolean isVarargs();
+
+ abstract Class[] getParamTypes();
+
+ abstract String getName();
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/CharacterOrString.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/CharacterOrString.java b/src/main/java/org/apache/freemarker/core/model/impl/CharacterOrString.java
new file mode 100644
index 0000000..0e31d80
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/CharacterOrString.java
@@ -0,0 +1,45 @@
+/*
+ * 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.model.TemplateScalarModel;
+
+/**
+ * Represents value unwrapped both to {@link Character} and {@link String}. This is needed for unwrapped overloaded
+ * method parameters where both {@link Character} and {@link String} occurs on the same parameter position when the
+ * {@link TemplateScalarModel} to unwrapp contains a {@link String} of length 1.
+ */
+final class CharacterOrString {
+
+ private final String stringValue;
+
+ CharacterOrString(String stringValue) {
+ this.stringValue = stringValue;
+ }
+
+ String getAsString() {
+ return stringValue;
+ }
+
+ char getAsChar() {
+ return stringValue.charAt(0);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java b/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java
new file mode 100644
index 0000000..8b72f2b
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java
@@ -0,0 +1,148 @@
+/*
+ * 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.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util._ClassUtil;
+
+/**
+ * Base class for hash models keyed by Java class names.
+ */
+abstract class ClassBasedModelFactory implements TemplateHashModel {
+ private final DefaultObjectWrapper wrapper;
+
+ private final Map/*<String,TemplateModel>*/ cache = new ConcurrentHashMap();
+ private final Set classIntrospectionsInProgress = new HashSet();
+
+ protected ClassBasedModelFactory(DefaultObjectWrapper wrapper) {
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ try {
+ return getInternal(key);
+ } catch (Exception e) {
+ if (e instanceof TemplateModelException) {
+ throw (TemplateModelException) e;
+ } else {
+ throw new TemplateModelException(e);
+ }
+ }
+ }
+
+ private TemplateModel getInternal(String key) throws TemplateModelException, ClassNotFoundException {
+ {
+ TemplateModel model = (TemplateModel) cache.get(key);
+ if (model != null) return model;
+ }
+
+ final ClassIntrospector classIntrospector;
+ int classIntrospectorClearingCounter;
+ final Object sharedLock = wrapper.getSharedIntrospectionLock();
+ synchronized (sharedLock) {
+ TemplateModel model = (TemplateModel) cache.get(key);
+ if (model != null) return model;
+
+ while (model == null
+ && classIntrospectionsInProgress.contains(key)) {
+ // Another thread is already introspecting this class;
+ // waiting for its result.
+ try {
+ sharedLock.wait();
+ model = (TemplateModel) cache.get(key);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(
+ "Class inrospection data lookup aborded: " + e);
+ }
+ }
+ if (model != null) return model;
+
+ // This will be the thread that introspects this class.
+ classIntrospectionsInProgress.add(key);
+
+ // While the classIntrospector should not be changed from another thread, badly written apps can do that,
+ // and it's cheap to get the classIntrospector from inside the lock here:
+ classIntrospector = wrapper.getClassIntrospector();
+ classIntrospectorClearingCounter = classIntrospector.getClearingCounter();
+ }
+ try {
+ final Class clazz = _ClassUtil.forName(key);
+
+ // This is called so that we trigger the
+ // class-reloading detector. If clazz is a reloaded class,
+ // the wrapper will in turn call our clearCache method.
+ // TODO: Why do we check it now and only now?
+ classIntrospector.get(clazz);
+
+ TemplateModel model = createModel(clazz);
+ // Warning: model will be null if the class is not good for the subclass.
+ // For example, EnumModels#createModel returns null if clazz is not an enum.
+
+ if (model != null) {
+ synchronized (sharedLock) {
+ // Save it into the cache, but only if nothing relevant has changed while we were outside the lock:
+ if (classIntrospector == wrapper.getClassIntrospector()
+ && classIntrospectorClearingCounter == classIntrospector.getClearingCounter()) {
+ cache.put(key, model);
+ }
+ }
+ }
+ return model;
+ } finally {
+ synchronized (sharedLock) {
+ classIntrospectionsInProgress.remove(key);
+ sharedLock.notifyAll();
+ }
+ }
+ }
+
+ void clearCache() {
+ synchronized (wrapper.getSharedIntrospectionLock()) {
+ cache.clear();
+ }
+ }
+
+ void removeFromCache(Class clazz) {
+ synchronized (wrapper.getSharedIntrospectionLock()) {
+ cache.remove(clazz.getName());
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ protected abstract TemplateModel createModel(Class clazz)
+ throws TemplateModelException;
+
+ protected DefaultObjectWrapper getWrapper() {
+ return wrapper;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/ClassChangeNotifier.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ClassChangeNotifier.java b/src/main/java/org/apache/freemarker/core/model/impl/ClassChangeNotifier.java
new file mode 100644
index 0000000..ddca496
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ClassChangeNotifier.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Reports when the non-private interface of a class was changed to the subscribers.
+ */
+interface ClassChangeNotifier {
+
+ /**
+ * @param classIntrospector Should only be weak-referenced from the monitor object.
+ */
+ void subscribe(ClassIntrospector classIntrospector);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java b/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
new file mode 100644
index 0000000..4dd8931
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
@@ -0,0 +1,807 @@
+/*
+ * 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.IndexedPropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.MethodDescriptor;
+import java.beans.PropertyDescriptor;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.freemarker.core._CoreLogs;
+import org.apache.freemarker.core.model.impl.MethodAppearanceFineTuner.DecisionInput;
+import org.apache.freemarker.core.util.BugException;
+import org.apache.freemarker.core.util._NullArgumentException;
+import org.slf4j.Logger;
+
+/**
+ * Returns information about a {@link Class} that's useful for FreeMarker. Encapsulates a cache for this. Thread-safe,
+ * doesn't even require "proper publishing" starting from 2.3.24 or Java 5. Immutable, with the exception of the
+ * internal caches.
+ *
+ * <p>
+ * Note that instances of this are cached on the level of FreeMarker's defining class loader. Hence, it must not do
+ * operations that depend on the Thread Context Class Loader, such as resolving class names.
+ */
+class ClassIntrospector {
+
+ // Attention: This class must be thread-safe (not just after proper publishing). This is important as some of
+ // these are shared by many object wrappers, and concurrency related glitches due to user errors must remain
+ // local to the object wrappers, not corrupting the shared ClassIntrospector.
+
+ private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER;
+
+ private static final String JREBEL_SDK_CLASS_NAME = "org.zeroturnaround.javarebel.ClassEventListener";
+ private static final String JREBEL_INTEGRATION_ERROR_MSG
+ = "Error initializing JRebel integration. JRebel integration disabled.";
+
+ private static final ClassChangeNotifier CLASS_CHANGE_NOTIFIER;
+ static {
+ boolean jRebelAvailable;
+ try {
+ Class.forName(JREBEL_SDK_CLASS_NAME);
+ jRebelAvailable = true;
+ } catch (Throwable e) {
+ jRebelAvailable = false;
+ try {
+ if (!(e instanceof ClassNotFoundException)) {
+ LOG.error(JREBEL_INTEGRATION_ERROR_MSG, e);
+ }
+ } catch (Throwable loggingE) {
+ // ignore
+ }
+ }
+
+ ClassChangeNotifier classChangeNotifier;
+ if (jRebelAvailable) {
+ try {
+ classChangeNotifier = (ClassChangeNotifier)
+ Class.forName("org.apache.freemarker.core.model.impl.JRebelClassChangeNotifier").newInstance();
+ } catch (Throwable e) {
+ classChangeNotifier = null;
+ try {
+ LOG.error(JREBEL_INTEGRATION_ERROR_MSG, e);
+ } catch (Throwable loggingE) {
+ // ignore
+ }
+ }
+ } else {
+ classChangeNotifier = null;
+ }
+
+ CLASS_CHANGE_NOTIFIER = classChangeNotifier;
+ }
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Introspection info Map keys:
+
+ /** Key in the class info Map to the Map that maps method to argument type arrays */
+ private static final Object ARG_TYPES_BY_METHOD_KEY = new Object();
+ /** Key in the class info Map to the object that represents the constructors (one or multiple due to overloading) */
+ static final Object CONSTRUCTORS_KEY = new Object();
+ /** Key in the class info Map to the get(String|Object) Method */
+ static final Object GENERIC_GET_KEY = new Object();
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Introspection configuration properties:
+
+ // Note: These all must be *declared* final (or else synchronization is needed everywhere where they are accessed).
+
+ final int exposureLevel;
+ final boolean exposeFields;
+ final MethodAppearanceFineTuner methodAppearanceFineTuner;
+ final MethodSorter methodSorter;
+
+ /** See {@link #getHasSharedInstanceRestrictons()} */
+ final private boolean hasSharedInstanceRestrictons;
+
+ /** See {@link #isShared()} */
+ final private boolean shared;
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // State fields:
+
+ private final Object sharedLock;
+ private final Map<Class<?>, Map<Object, Object>> cache
+ = new ConcurrentHashMap<>(0, 0.75f, 16);
+ private final Set<String> cacheClassNames = new HashSet<>(0);
+ private final Set<Class<?>> classIntrospectionsInProgress = new HashSet<>(0);
+
+ private final List<WeakReference<Object/*ClassBasedModelFactory|ModelCache>*/>> modelFactories
+ = new LinkedList<>();
+ private final ReferenceQueue<Object> modelFactoriesRefQueue = new ReferenceQueue<>();
+
+ private int clearingCounter;
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Instantiation:
+
+ /**
+ * Creates a new instance, that is hence surely not shared (singleton) instance.
+ *
+ * @param pa
+ * Stores what the values of the JavaBean properties of the returned instance will be. Not {@code null}.
+ */
+ ClassIntrospector(ClassIntrospectorBuilder pa, Object sharedLock) {
+ this(pa, sharedLock, false, false);
+ }
+
+ /**
+ * @param hasSharedInstanceRestrictons
+ * {@code true} exactly if we are creating a new instance with {@link ClassIntrospectorBuilder}. Then
+ * it's {@code true} even if it won't put the instance into the cache.
+ */
+ ClassIntrospector(ClassIntrospectorBuilder builder, Object sharedLock,
+ boolean hasSharedInstanceRestrictons, boolean shared) {
+ _NullArgumentException.check("sharedLock", sharedLock);
+
+ exposureLevel = builder.getExposureLevel();
+ exposeFields = builder.getExposeFields();
+ methodAppearanceFineTuner = builder.getMethodAppearanceFineTuner();
+ methodSorter = builder.getMethodSorter();
+
+ this.sharedLock = sharedLock;
+
+ this.hasSharedInstanceRestrictons = hasSharedInstanceRestrictons;
+ this.shared = shared;
+
+ if (CLASS_CHANGE_NOTIFIER != null) {
+ CLASS_CHANGE_NOTIFIER.subscribe(this);
+ }
+ }
+
+ /**
+ * Returns a {@link ClassIntrospectorBuilder}-s that could be used to create an identical {@link #ClassIntrospector}
+ * . The returned {@link ClassIntrospectorBuilder} can be modified without interfering with anything.
+ */
+ ClassIntrospectorBuilder getPropertyAssignments() {
+ return new ClassIntrospectorBuilder(this);
+ }
+
+ // ------------------------------------------------------------------------------------------------------------------
+ // Introspection:
+
+ /**
+ * Gets the class introspection data from {@link #cache}, automatically creating the cache entry if it's missing.
+ *
+ * @return A {@link Map} where each key is a property/method/field name (or a special {@link Object} key like
+ * {@link #CONSTRUCTORS_KEY}), each value is a {@link PropertyDescriptor} or {@link Method} or
+ * {@link OverloadedMethods} or {@link Field} (but better check the source code...).
+ */
+ Map<Object, Object> get(Class<?> clazz) {
+ {
+ Map<Object, Object> introspData = cache.get(clazz);
+ if (introspData != null) return introspData;
+ }
+
+ String className;
+ synchronized (sharedLock) {
+ Map<Object, Object> introspData = cache.get(clazz);
+ if (introspData != null) return introspData;
+
+ className = clazz.getName();
+ if (cacheClassNames.contains(className)) {
+ onSameNameClassesDetected(className);
+ }
+
+ while (introspData == null && classIntrospectionsInProgress.contains(clazz)) {
+ // Another thread is already introspecting this class;
+ // waiting for its result.
+ try {
+ sharedLock.wait();
+ introspData = cache.get(clazz);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(
+ "Class inrospection data lookup aborded: " + e);
+ }
+ }
+ if (introspData != null) return introspData;
+
+ // This will be the thread that introspects this class.
+ classIntrospectionsInProgress.add(clazz);
+ }
+ try {
+ Map<Object, Object> introspData = createClassIntrospectionData(clazz);
+ synchronized (sharedLock) {
+ cache.put(clazz, introspData);
+ cacheClassNames.add(className);
+ }
+ return introspData;
+ } finally {
+ synchronized (sharedLock) {
+ classIntrospectionsInProgress.remove(clazz);
+ sharedLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Creates a {@link Map} with the content as described for the return value of {@link #get(Class)}.
+ */
+ private Map<Object, Object> createClassIntrospectionData(Class<?> clazz) {
+ final Map<Object, Object> introspData = new HashMap<>();
+
+ if (exposeFields) {
+ addFieldsToClassIntrospectionData(introspData, clazz);
+ }
+
+ final Map<MethodSignature, List<Method>> accessibleMethods = discoverAccessibleMethods(clazz);
+
+ addGenericGetToClassIntrospectionData(introspData, accessibleMethods);
+
+ if (exposureLevel != DefaultObjectWrapper.EXPOSE_NOTHING) {
+ try {
+ addBeanInfoToClassIntrospectionData(introspData, clazz, accessibleMethods);
+ } catch (IntrospectionException e) {
+ LOG.warn("Couldn't properly perform introspection for class {}", clazz.getName(), e);
+ introspData.clear(); // FIXME NBC: Don't drop everything here.
+ }
+ }
+
+ addConstructorsToClassIntrospectionData(introspData, clazz);
+
+ if (introspData.size() > 1) {
+ return introspData;
+ } else if (introspData.size() == 0) {
+ return Collections.emptyMap();
+ } else { // map.size() == 1
+ Entry<Object, Object> e = introspData.entrySet().iterator().next();
+ return Collections.singletonMap(e.getKey(), e.getValue());
+ }
+ }
+
+ private void addFieldsToClassIntrospectionData(Map<Object, Object> introspData, Class<?> clazz)
+ throws SecurityException {
+ for (Field field : clazz.getFields()) {
+ if ((field.getModifiers() & Modifier.STATIC) == 0) {
+ introspData.put(field.getName(), field);
+ }
+ }
+ }
+
+ private void addBeanInfoToClassIntrospectionData(
+ Map<Object, Object> introspData, Class<?> clazz, Map<MethodSignature, List<Method>> accessibleMethods)
+ throws IntrospectionException {
+ BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
+
+ PropertyDescriptor[] pda = beanInfo.getPropertyDescriptors();
+ if (pda != null) {
+ int pdaLength = pda.length;
+ for (int i = pdaLength - 1; i >= 0; --i) {
+ addPropertyDescriptorToClassIntrospectionData(
+ introspData, pda[i], clazz,
+ accessibleMethods);
+ }
+ }
+
+ if (exposureLevel < DefaultObjectWrapper.EXPOSE_PROPERTIES_ONLY) {
+ final MethodAppearanceFineTuner.Decision decision = new MethodAppearanceFineTuner.Decision();
+ DecisionInput decisionInput = null;
+ final MethodDescriptor[] mda = sortMethodDescriptors(beanInfo.getMethodDescriptors());
+ if (mda != null) {
+ int mdaLength = mda.length;
+ for (int i = mdaLength - 1; i >= 0; --i) {
+ final MethodDescriptor md = mda[i];
+ final Method method = getMatchingAccessibleMethod(md.getMethod(), accessibleMethods);
+ if (method != null && isAllowedToExpose(method)) {
+ decision.setDefaults(method);
+ if (methodAppearanceFineTuner != null) {
+ if (decisionInput == null) {
+ decisionInput = new DecisionInput();
+ }
+ decisionInput.setContainingClass(clazz);
+ decisionInput.setMethod(method);
+
+ methodAppearanceFineTuner.process(decisionInput, decision);
+ }
+
+ PropertyDescriptor propDesc = decision.getExposeAsProperty();
+ if (propDesc != null && !(introspData.get(propDesc.getName()) instanceof PropertyDescriptor)) {
+ addPropertyDescriptorToClassIntrospectionData(
+ introspData, propDesc, clazz, accessibleMethods);
+ }
+
+ String methodKey = decision.getExposeMethodAs();
+ if (methodKey != null) {
+ Object previous = introspData.get(methodKey);
+ if (previous instanceof Method) {
+ // Overloaded method - replace Method with a OverloadedMethods
+ OverloadedMethods overloadedMethods = new OverloadedMethods();
+ overloadedMethods.addMethod((Method) previous);
+ overloadedMethods.addMethod(method);
+ introspData.put(methodKey, overloadedMethods);
+ // Remove parameter type information
+ getArgTypesByMethod(introspData).remove(previous);
+ } else if (previous instanceof OverloadedMethods) {
+ // Already overloaded method - add new overload
+ ((OverloadedMethods) previous).addMethod(method);
+ } else if (decision.getMethodShadowsProperty()
+ || !(previous instanceof PropertyDescriptor)) {
+ // Simple method (this far)
+ introspData.put(methodKey, method);
+ getArgTypesByMethod(introspData).put(method,
+ method.getParameterTypes());
+ }
+ }
+ }
+ } // for each in mda
+ } // if mda != null
+ } // end if (exposureLevel < EXPOSE_PROPERTIES_ONLY)
+ }
+
+ private void addPropertyDescriptorToClassIntrospectionData(Map<Object, Object> introspData,
+ PropertyDescriptor pd, Class<?> clazz, Map<MethodSignature, List<Method>> accessibleMethods) {
+ if (pd instanceof IndexedPropertyDescriptor) {
+ IndexedPropertyDescriptor ipd =
+ (IndexedPropertyDescriptor) pd;
+ Method readMethod = ipd.getIndexedReadMethod();
+ Method publicReadMethod = getMatchingAccessibleMethod(readMethod, accessibleMethods);
+ if (publicReadMethod != null && isAllowedToExpose(publicReadMethod)) {
+ try {
+ if (readMethod != publicReadMethod) {
+ ipd = new IndexedPropertyDescriptor(
+ ipd.getName(), ipd.getReadMethod(),
+ null, publicReadMethod,
+ null);
+ }
+ introspData.put(ipd.getName(), ipd);
+ getArgTypesByMethod(introspData).put(publicReadMethod, publicReadMethod.getParameterTypes());
+ } catch (IntrospectionException e) {
+ LOG.warn("Failed creating a publicly-accessible property descriptor "
+ + "for {} indexed property {}, read method {}",
+ clazz.getName(), pd.getName(), publicReadMethod,
+ e);
+ }
+ }
+ } else {
+ Method readMethod = pd.getReadMethod();
+ Method publicReadMethod = getMatchingAccessibleMethod(readMethod, accessibleMethods);
+ if (publicReadMethod != null && isAllowedToExpose(publicReadMethod)) {
+ try {
+ if (readMethod != publicReadMethod) {
+ pd = new PropertyDescriptor(pd.getName(), publicReadMethod, null);
+ pd.setReadMethod(publicReadMethod);
+ }
+ introspData.put(pd.getName(), pd);
+ } catch (IntrospectionException e) {
+ LOG.warn("Failed creating a publicly-accessible property descriptor "
+ + "for {} property {}, read method {}",
+ clazz.getName(), pd.getName(), publicReadMethod,
+ e);
+ }
+ }
+ }
+ }
+
+ private void addGenericGetToClassIntrospectionData(Map<Object, Object> introspData,
+ Map<MethodSignature, List<Method>> accessibleMethods) {
+ Method genericGet = getFirstAccessibleMethod(
+ MethodSignature.GET_STRING_SIGNATURE, accessibleMethods);
+ if (genericGet == null) {
+ genericGet = getFirstAccessibleMethod(
+ MethodSignature.GET_OBJECT_SIGNATURE, accessibleMethods);
+ }
+ if (genericGet != null) {
+ introspData.put(GENERIC_GET_KEY, genericGet);
+ }
+ }
+
+ private void addConstructorsToClassIntrospectionData(final Map<Object, Object> introspData,
+ Class<?> clazz) {
+ try {
+ Constructor<?>[] ctors = clazz.getConstructors();
+ if (ctors.length == 1) {
+ Constructor<?> ctor = ctors[0];
+ introspData.put(CONSTRUCTORS_KEY, new SimpleMethod(ctor, ctor.getParameterTypes()));
+ } else if (ctors.length > 1) {
+ OverloadedMethods overloadedCtors = new OverloadedMethods();
+ for (Constructor<?> ctor : ctors) {
+ overloadedCtors.addConstructor(ctor);
+ }
+ introspData.put(CONSTRUCTORS_KEY, overloadedCtors);
+ }
+ } catch (SecurityException e) {
+ LOG.warn("Can't discover constructors for class {}", clazz.getName(), e);
+ }
+ }
+
+ /**
+ * Retrieves mapping of {@link MethodSignature}-s to a {@link List} of accessible methods for a class. In case the
+ * class is not public, retrieves methods with same signature as its public methods from public superclasses and
+ * interfaces. Basically upcasts every method to the nearest accessible method.
+ */
+ private static Map<MethodSignature, List<Method>> discoverAccessibleMethods(Class<?> clazz) {
+ Map<MethodSignature, List<Method>> accessibles = new HashMap<>();
+ discoverAccessibleMethods(clazz, accessibles);
+ return accessibles;
+ }
+
+ private static void discoverAccessibleMethods(Class<?> clazz, Map<MethodSignature, List<Method>> accessibles) {
+ if (Modifier.isPublic(clazz.getModifiers())) {
+ try {
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ MethodSignature sig = new MethodSignature(method);
+ // Contrary to intuition, a class can actually have several
+ // different methods with same signature *but* different
+ // return types. These can't be constructed using Java the
+ // language, as this is illegal on source code level, but
+ // the compiler can emit synthetic methods as part of
+ // generic type reification that will have same signature
+ // yet different return type than an existing explicitly
+ // declared method. Consider:
+ // public interface I<T> { T m(); }
+ // public class C implements I<Integer> { Integer m() { return 42; } }
+ // C.class will have both "Object m()" and "Integer m()" methods.
+ List<Method> methodList = accessibles.get(sig);
+ if (methodList == null) {
+ // TODO Collection.singletonList is more efficient, though read only.
+ methodList = new LinkedList<>();
+ accessibles.put(sig, methodList);
+ }
+ methodList.add(method);
+ }
+ return;
+ } catch (SecurityException e) {
+ LOG.warn("Could not discover accessible methods of class {}, attemping superclasses/interfaces.",
+ clazz.getName(), e);
+ // Fall through and attempt to discover superclass/interface methods
+ }
+ }
+
+ Class<?>[] interfaces = clazz.getInterfaces();
+ for (Class<?> anInterface : interfaces) {
+ discoverAccessibleMethods(anInterface, accessibles);
+ }
+ Class<?> superclass = clazz.getSuperclass();
+ if (superclass != null) {
+ discoverAccessibleMethods(superclass, accessibles);
+ }
+ }
+
+ private static Method getMatchingAccessibleMethod(Method m, Map<MethodSignature, List<Method>> accessibles) {
+ if (m == null) {
+ return null;
+ }
+ MethodSignature sig = new MethodSignature(m);
+ List<Method> ams = accessibles.get(sig);
+ if (ams == null) {
+ return null;
+ }
+ for (Method am : ams) {
+ if (am.getReturnType() == m.getReturnType()) {
+ return am;
+ }
+ }
+ return null;
+ }
+
+ private static Method getFirstAccessibleMethod(MethodSignature sig, Map<MethodSignature, List<Method>> accessibles) {
+ List<Method> ams = accessibles.get(sig);
+ if (ams == null || ams.isEmpty()) {
+ return null;
+ }
+ return ams.get(0);
+ }
+
+ /**
+ * As of this writing, this is only used for testing if method order really doesn't mater.
+ */
+ private MethodDescriptor[] sortMethodDescriptors(MethodDescriptor[] methodDescriptors) {
+ return methodSorter != null ? methodSorter.sortMethodDescriptors(methodDescriptors) : methodDescriptors;
+ }
+
+ boolean isAllowedToExpose(Method method) {
+ return exposureLevel < DefaultObjectWrapper.EXPOSE_SAFE || !UnsafeMethods.isUnsafeMethod(method);
+ }
+
+ private static Map<Method, Class<?>[]> getArgTypesByMethod(Map<Object, Object> classInfo) {
+ @SuppressWarnings("unchecked")
+ Map<Method, Class<?>[]> argTypes = (Map<Method, Class<?>[]>) classInfo.get(ARG_TYPES_BY_METHOD_KEY);
+ if (argTypes == null) {
+ argTypes = new HashMap<>();
+ classInfo.put(ARG_TYPES_BY_METHOD_KEY, argTypes);
+ }
+ return argTypes;
+ }
+
+ private static final class MethodSignature {
+ private static final MethodSignature GET_STRING_SIGNATURE =
+ new MethodSignature("get", new Class[] { String.class });
+ private static final MethodSignature GET_OBJECT_SIGNATURE =
+ new MethodSignature("get", new Class[] { Object.class });
+
+ private final String name;
+ private final Class<?>[] args;
+
+ private MethodSignature(String name, Class<?>[] args) {
+ this.name = name;
+ this.args = args;
+ }
+
+ MethodSignature(Method method) {
+ this(method.getName(), method.getParameterTypes());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof MethodSignature) {
+ MethodSignature ms = (MethodSignature) o;
+ return ms.name.equals(name) && Arrays.equals(args, ms.args);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode() ^ args.length; // TODO That's a poor quality hash... isn't this a problem?
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Cache management:
+
+ /**
+ * Corresponds to {@link DefaultObjectWrapper#clearClassIntrospecitonCache()}.
+ *
+ * @since 2.3.20
+ */
+ void clearCache() {
+ if (getHasSharedInstanceRestrictons()) {
+ throw new IllegalStateException(
+ "It's not allowed to clear the whole cache in a read-only " + getClass().getName() +
+ "instance. Use removeFromClassIntrospectionCache(String prefix) instead.");
+ }
+ forcedClearCache();
+ }
+
+ private void forcedClearCache() {
+ synchronized (sharedLock) {
+ cache.clear();
+ cacheClassNames.clear();
+ clearingCounter++;
+
+ for (WeakReference<Object> regedMfREf : modelFactories) {
+ Object regedMf = regedMfREf.get();
+ if (regedMf != null) {
+ if (regedMf instanceof ClassBasedModelFactory) {
+ ((ClassBasedModelFactory) regedMf).clearCache();
+ } else if (regedMf instanceof ModelCache) {
+ ((ModelCache) regedMf).clearCache();
+ } else {
+ throw new BugException();
+ }
+ }
+ }
+
+ removeClearedModelFactoryReferences();
+ }
+ }
+
+ /**
+ * Corresponds to {@link DefaultObjectWrapper#removeFromClassIntrospectionCache(Class)}.
+ *
+ * @since 2.3.20
+ */
+ void remove(Class<?> clazz) {
+ synchronized (sharedLock) {
+ cache.remove(clazz);
+ cacheClassNames.remove(clazz.getName());
+ clearingCounter++;
+
+ for (WeakReference<Object> regedMfREf : modelFactories) {
+ Object regedMf = regedMfREf.get();
+ if (regedMf != null) {
+ if (regedMf instanceof ClassBasedModelFactory) {
+ ((ClassBasedModelFactory) regedMf).removeFromCache(clazz);
+ } else if (regedMf instanceof ModelCache) {
+ ((ModelCache) regedMf).clearCache(); // doesn't support selective clearing ATM
+ } else {
+ throw new BugException();
+ }
+ }
+ }
+
+ removeClearedModelFactoryReferences();
+ }
+ }
+
+ /**
+ * Returns the number of events so far that could make class introspection data returned earlier outdated.
+ */
+ int getClearingCounter() {
+ synchronized (sharedLock) {
+ return clearingCounter;
+ }
+ }
+
+ private void onSameNameClassesDetected(String className) {
+ // TODO: This behavior should be pluggable, as in environments where
+ // some classes are often reloaded or multiple versions of the
+ // same class is normal (OSGi), this will drop the cache contents
+ // too often.
+ LOG.info(
+ "Detected multiple classes with the same name, \"{}\". "
+ + "Assuming it was a class-reloading. Clearing class introspection caches to release old data.",
+ className);
+ forcedClearCache();
+ }
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Managing dependent objects:
+
+ void registerModelFactory(ClassBasedModelFactory mf) {
+ registerModelFactory((Object) mf);
+ }
+
+ void registerModelFactory(ModelCache mf) {
+ registerModelFactory((Object) mf);
+ }
+
+ private void registerModelFactory(Object mf) {
+ // Note that this `synchronized (sharedLock)` is also need for the DefaultObjectWrapper constructor to work safely.
+ synchronized (sharedLock) {
+ modelFactories.add(new WeakReference<>(mf, modelFactoriesRefQueue));
+ removeClearedModelFactoryReferences();
+ }
+ }
+
+ void unregisterModelFactory(ClassBasedModelFactory mf) {
+ unregisterModelFactory((Object) mf);
+ }
+
+ void unregisterModelFactory(ModelCache mf) {
+ unregisterModelFactory((Object) mf);
+ }
+
+ void unregisterModelFactory(Object mf) {
+ synchronized (sharedLock) {
+ for (Iterator<WeakReference<Object>> it = modelFactories.iterator(); it.hasNext(); ) {
+ Object regedMf = it.next().get();
+ if (regedMf == mf) {
+ it.remove();
+ }
+ }
+
+ }
+ }
+
+ private void removeClearedModelFactoryReferences() {
+ Reference<?> cleardRef;
+ while ((cleardRef = modelFactoriesRefQueue.poll()) != null) {
+ synchronized (sharedLock) {
+ findClearedRef: for (Iterator<WeakReference<Object>> it = modelFactories.iterator(); it.hasNext(); ) {
+ if (it.next() == cleardRef) {
+ it.remove();
+ break findClearedRef;
+ }
+ }
+ }
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Extracting from introspection info:
+
+ static Class<?>[] getArgTypes(Map<Object, Object> classInfo, Method method) {
+ @SuppressWarnings("unchecked")
+ Map<Method, Class<?>[]> argTypesByMethod = (Map<Method, Class<?>[]>) classInfo.get(ARG_TYPES_BY_METHOD_KEY);
+ return argTypesByMethod.get(method);
+ }
+
+ /**
+ * Returns the number of introspected methods/properties that should be available via the TemplateHashModel
+ * interface.
+ */
+ int keyCount(Class<?> clazz) {
+ Map<Object, Object> map = get(clazz);
+ int count = map.size();
+ if (map.containsKey(CONSTRUCTORS_KEY)) count--;
+ if (map.containsKey(GENERIC_GET_KEY)) count--;
+ if (map.containsKey(ARG_TYPES_BY_METHOD_KEY)) count--;
+ return count;
+ }
+
+ /**
+ * Returns the Set of names of introspected methods/properties that should be available via the TemplateHashModel
+ * interface.
+ */
+ Set<Object> keySet(Class<?> clazz) {
+ Set<Object> set = new HashSet<>(get(clazz).keySet());
+ set.remove(CONSTRUCTORS_KEY);
+ set.remove(GENERIC_GET_KEY);
+ set.remove(ARG_TYPES_BY_METHOD_KEY);
+ return set;
+ }
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Properties
+
+ int getExposureLevel() {
+ return exposureLevel;
+ }
+
+ boolean getExposeFields() {
+ return exposeFields;
+ }
+
+ MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
+ return methodAppearanceFineTuner;
+ }
+
+ MethodSorter getMethodSorter() {
+ return methodSorter;
+ }
+
+ /**
+ * Returns {@code true} if this instance was created with {@link ClassIntrospectorBuilder}, even if it wasn't
+ * actually put into the cache (as we reserve the right to do so in later versions).
+ */
+ boolean getHasSharedInstanceRestrictons() {
+ return hasSharedInstanceRestrictons;
+ }
+
+ /**
+ * Tells if this instance is (potentially) shared among {@link DefaultObjectWrapper} instances.
+ *
+ * @see #getHasSharedInstanceRestrictons()
+ */
+ boolean isShared() {
+ return shared;
+ }
+
+ /**
+ * Almost always, you want to use {@link DefaultObjectWrapper#getSharedIntrospectionLock()}, not this! The only exception is
+ * when you get this to set the field returned by {@link DefaultObjectWrapper#getSharedIntrospectionLock()}.
+ */
+ Object getSharedLock() {
+ return sharedLock;
+ }
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Monitoring:
+
+ /** For unit testing only */
+ Object[] getRegisteredModelFactoriesSnapshot() {
+ synchronized (sharedLock) {
+ return modelFactories.toArray();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospectorBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospectorBuilder.java b/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospectorBuilder.java
new file mode 100644
index 0000000..b17a49e
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospectorBuilder.java
@@ -0,0 +1,190 @@
+/*
+ * 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.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core.util._NullArgumentException;
+
+final class ClassIntrospectorBuilder implements Cloneable {
+
+ private static final Map/*<PropertyAssignments, Reference<ClassIntrospector>>*/ INSTANCE_CACHE = new HashMap();
+ private static final ReferenceQueue INSTANCE_CACHE_REF_QUEUE = new ReferenceQueue();
+
+ // Properties and their *defaults*:
+ private int exposureLevel = DefaultObjectWrapper.EXPOSE_SAFE;
+ private boolean exposeFields;
+ private MethodAppearanceFineTuner methodAppearanceFineTuner;
+ private MethodSorter methodSorter;
+ // Attention:
+ // - This is also used as a cache key, so non-normalized field values should be avoided.
+ // - If some field has a default value, it must be set until the end of the constructor. No field that has a
+ // default can be left unset (like null).
+ // - If you add a new field, review all methods in this class, also the ClassIntrospector constructor
+
+ ClassIntrospectorBuilder(ClassIntrospector ci) {
+ exposureLevel = ci.exposureLevel;
+ exposeFields = ci.exposeFields;
+ methodAppearanceFineTuner = ci.methodAppearanceFineTuner;
+ methodSorter = ci.methodSorter;
+ }
+
+ ClassIntrospectorBuilder(Version incompatibleImprovements) {
+ // Warning: incompatibleImprovements must not affect this object at versions increments where there's no
+ // change in the DefaultObjectWrapper.normalizeIncompatibleImprovements results. That is, this class may don't react
+ // to some version changes that affects DefaultObjectWrapper, but not the other way around.
+ _NullArgumentException.check(incompatibleImprovements);
+ // Currently nothing depends on incompatibleImprovements
+ }
+
+ @Override
+ protected Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException("Failed to clone ClassIntrospectorBuilder", e);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (exposeFields ? 1231 : 1237);
+ result = prime * result + exposureLevel;
+ result = prime * result + System.identityHashCode(methodAppearanceFineTuner);
+ result = prime * result + System.identityHashCode(methodSorter);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ ClassIntrospectorBuilder other = (ClassIntrospectorBuilder) obj;
+
+ if (exposeFields != other.exposeFields) return false;
+ if (exposureLevel != other.exposureLevel) return false;
+ if (methodAppearanceFineTuner != other.methodAppearanceFineTuner) return false;
+ return methodSorter == other.methodSorter;
+ }
+
+ public int getExposureLevel() {
+ return exposureLevel;
+ }
+
+ /** See {@link DefaultObjectWrapper#setExposureLevel(int)}. */
+ public void setExposureLevel(int exposureLevel) {
+ if (exposureLevel < DefaultObjectWrapper.EXPOSE_ALL || exposureLevel > DefaultObjectWrapper.EXPOSE_NOTHING) {
+ throw new IllegalArgumentException("Illegal exposure level: " + exposureLevel);
+ }
+
+ this.exposureLevel = exposureLevel;
+ }
+
+ public boolean getExposeFields() {
+ return exposeFields;
+ }
+
+ /** See {@link DefaultObjectWrapper#setExposeFields(boolean)}. */
+ public void setExposeFields(boolean exposeFields) {
+ this.exposeFields = exposeFields;
+ }
+
+ public MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
+ return methodAppearanceFineTuner;
+ }
+
+ public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner methodAppearanceFineTuner) {
+ this.methodAppearanceFineTuner = methodAppearanceFineTuner;
+ }
+
+ public MethodSorter getMethodSorter() {
+ return methodSorter;
+ }
+
+ public void setMethodSorter(MethodSorter methodSorter) {
+ this.methodSorter = methodSorter;
+ }
+
+ private static void removeClearedReferencesFromInstanceCache() {
+ Reference clearedRef;
+ while ((clearedRef = INSTANCE_CACHE_REF_QUEUE.poll()) != null) {
+ synchronized (INSTANCE_CACHE) {
+ findClearedRef: for (Iterator it = INSTANCE_CACHE.values().iterator(); it.hasNext(); ) {
+ if (it.next() == clearedRef) {
+ it.remove();
+ break findClearedRef;
+ }
+ }
+ }
+ }
+ }
+
+ /** For unit testing only */
+ static void clearInstanceCache() {
+ synchronized (INSTANCE_CACHE) {
+ INSTANCE_CACHE.clear();
+ }
+ }
+
+ /** For unit testing only */
+ static Map getInstanceCache() {
+ return INSTANCE_CACHE;
+ }
+
+ /**
+ * Returns an instance that is possibly shared (singleton). Note that this comes with its own "shared lock",
+ * since everyone who uses this object will have to lock with that common object.
+ */
+ ClassIntrospector build() {
+ if ((methodAppearanceFineTuner == null || methodAppearanceFineTuner instanceof SingletonCustomizer)
+ && (methodSorter == null || methodSorter instanceof SingletonCustomizer)) {
+ // Instance can be cached.
+ ClassIntrospector instance;
+ synchronized (INSTANCE_CACHE) {
+ Reference instanceRef = (Reference) INSTANCE_CACHE.get(this);
+ instance = instanceRef != null ? (ClassIntrospector) instanceRef.get() : null;
+ if (instance == null) {
+ ClassIntrospectorBuilder thisClone = (ClassIntrospectorBuilder) clone(); // prevent any aliasing issues
+ instance = new ClassIntrospector(thisClone, new Object(), true, true);
+ INSTANCE_CACHE.put(thisClone, new WeakReference(instance, INSTANCE_CACHE_REF_QUEUE));
+ }
+ }
+
+ removeClearedReferencesFromInstanceCache();
+
+ return instance;
+ } else {
+ // If methodAppearanceFineTuner or methodSorter is specified and isn't marked as a singleton, the
+ // ClassIntrospector can't be shared/cached as those objects could contain a back-reference to the
+ // DefaultObjectWrapper.
+ return new ClassIntrospector(this, new Object(), true, false);
+ }
+ }
+
+}
\ 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/CollectionAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/CollectionAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/CollectionAdapter.java
new file mode 100644
index 0000000..70caa66
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/CollectionAdapter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.util.AbstractCollection;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+
+/**
+ * Adapts a {@link TemplateCollectionModel} to {@link Collection}.
+ */
+class CollectionAdapter extends AbstractCollection implements TemplateModelAdapter {
+ private final DefaultObjectWrapper wrapper;
+ private final TemplateCollectionModel model;
+
+ CollectionAdapter(TemplateCollectionModel model, DefaultObjectWrapper wrapper) {
+ this.model = model;
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public TemplateModel getTemplateModel() {
+ return model;
+ }
+
+ @Override
+ public int size() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Iterator iterator() {
+ try {
+ return new Iterator() {
+ final TemplateModelIterator i = model.iterator();
+
+ @Override
+ public boolean hasNext() {
+ try {
+ return i.hasNext();
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public Object next() {
+ try {
+ return wrapper.unwrap(i.next());
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/CollectionModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/CollectionModel.java b/src/main/java/org/apache/freemarker/core/model/impl/CollectionModel.java
new file mode 100644
index 0000000..36c6947
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/CollectionModel.java
@@ -0,0 +1,109 @@
+/*
+ * 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.util.Collection;
+import java.util.List;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+
+/**
+ * <p>A special case of {@link BeanModel} that can wrap Java collections
+ * and that implements the {@link TemplateCollectionModel} in order to be usable
+ * in a <tt><#list></tt> block.</p>
+ */
+public class CollectionModel
+extends
+ StringModel
+implements
+ TemplateCollectionModel,
+ TemplateSequenceModel {
+ static final ModelFactory FACTORY =
+ new ModelFactory()
+ {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new CollectionModel((Collection) object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+
+ /**
+ * Creates a new model that wraps the specified collection object.
+ * @param collection the collection object to wrap into a model.
+ * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+ * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+ * model gains many attributes from its wrapper, including the caching
+ * behavior, method exposure level, method-over-item shadowing policy etc.
+ */
+ public CollectionModel(Collection collection, DefaultObjectWrapper wrapper) {
+ super(collection, wrapper);
+ }
+
+ /**
+ * Retrieves the i-th object from the collection, wrapped as a TemplateModel.
+ * @throws TemplateModelException if the index is out of bounds, or the
+ * underlying collection is not a List.
+ */
+ @Override
+ public TemplateModel get(int index)
+ throws TemplateModelException {
+ // Don't forget to keep getSupportsIndexedAccess in sync with this!
+ if (object instanceof List) {
+ try {
+ return wrap(((List) object).get(index));
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+// throw new TemplateModelException("Index out of bounds: " + index);
+ }
+ } else {
+ throw new TemplateModelException("Underlying collection is not a list, it's " + object.getClass().getName());
+ }
+ }
+
+ /**
+ * Tells if {@link #get(int)} will always fail for this object.
+ * As this object implements {@link TemplateSequenceModel},
+ * {@link #get(int)} should always work, but due to a design flaw, for
+ * non-{@link List} wrapped objects {@link #get(int)} will always fail.
+ * This method exists to ease working this problem around.
+ *
+ * @since 2.3.17
+ */
+ public boolean getSupportsIndexedAccess() {
+ return object instanceof List;
+ }
+
+ @Override
+ public TemplateModelIterator iterator() {
+ return new IteratorModel(((Collection) object).iterator(), wrapper);
+ }
+
+ @Override
+ public int size() {
+ return ((Collection) object).size();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/DateModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DateModel.java b/src/main/java/org/apache/freemarker/core/model/impl/DateModel.java
new file mode 100644
index 0000000..e16e375
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/DateModel.java
@@ -0,0 +1,76 @@
+/*
+ * 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.util.Date;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateModel;
+
+/**
+ * Wraps arbitrary subclass of {@link java.util.Date} into a reflective model.
+ * Beside acting as a {@link TemplateDateModel}, you can call all Java methods
+ * on these objects as well.
+ */
+public class DateModel extends BeanModel implements
+ TemplateDateModel {
+ static final ModelFactory FACTORY =
+ new ModelFactory()
+ {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new DateModel((Date) object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+ private final int type;
+
+ /**
+ * Creates a new model that wraps the specified date object.
+ * @param date the date object to wrap into a model.
+ * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+ * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+ * model gains many attributes from its wrapper, including the caching
+ * behavior, method exposure level, method-over-item shadowing policy etc.
+ */
+ public DateModel(Date date, DefaultObjectWrapper wrapper) {
+ super(date, wrapper);
+ if (date instanceof java.sql.Date) {
+ type = DATE;
+ } else if (date instanceof java.sql.Time) {
+ type = TIME;
+ } else if (date instanceof java.sql.Timestamp) {
+ type = DATETIME;
+ } else {
+ type = wrapper.getDefaultDateType();
+ }
+ }
+
+ @Override
+ public Date getAsDate() {
+ return (Date) object;
+ }
+
+ @Override
+ public int getDateType() {
+ return type;
+ }
+}
[09/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/PrimtiveArrayBackedReadOnlyList.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/PrimtiveArrayBackedReadOnlyList.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/PrimtiveArrayBackedReadOnlyList.java
deleted file mode 100644
index 2e8850d..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/PrimtiveArrayBackedReadOnlyList.java
+++ /dev/null
@@ -1,47 +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.Array;
-import java.util.AbstractList;
-
-/**
- * Similar to {@link NonPrimitiveArrayBackedReadOnlyList}, but uses reflection so that it works with primitive arrays
- * too.
- */
-class PrimtiveArrayBackedReadOnlyList extends AbstractList {
-
- private final Object array;
-
- PrimtiveArrayBackedReadOnlyList(Object array) {
- this.array = array;
- }
-
- @Override
- public Object get(int index) {
- return Array.get(array, index);
- }
-
- @Override
- public int size() {
- return Array.getLength(array);
- }
-
-}
\ 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/ReflectionCallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ReflectionCallableMemberDescriptor.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ReflectionCallableMemberDescriptor.java
deleted file mode 100644
index 5cf0676..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ReflectionCallableMemberDescriptor.java
+++ /dev/null
@@ -1,95 +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 org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * The most commonly used {@link CallableMemberDescriptor} implementation.
- */
-final class ReflectionCallableMemberDescriptor extends CallableMemberDescriptor {
-
- private final Member/*Method|Constructor*/ member;
-
- /**
- * Don't modify this array!
- */
- final Class[] paramTypes;
-
- ReflectionCallableMemberDescriptor(Method member, Class[] paramTypes) {
- this.member = member;
- this.paramTypes = paramTypes;
- }
-
- ReflectionCallableMemberDescriptor(Constructor member, Class[] paramTypes) {
- this.member = member;
- this.paramTypes = paramTypes;
- }
-
- @Override
- TemplateModel invokeMethod(BeansWrapper bw, Object obj, Object[] args)
- throws TemplateModelException, InvocationTargetException, IllegalAccessException {
- return bw.invokeMethod(obj, (Method) member, args);
- }
-
- @Override
- Object invokeConstructor(BeansWrapper bw, Object[] args)
- throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
- return ((Constructor) member).newInstance(args);
- }
-
- @Override
- String getDeclaration() {
- return _MethodUtil.toString(member);
- }
-
- @Override
- boolean isConstructor() {
- return member instanceof Constructor;
- }
-
- @Override
- boolean isStatic() {
- return (member.getModifiers() & Modifier.STATIC) != 0;
- }
-
- @Override
- boolean isVarargs() {
- return _MethodUtil.isVarargs(member);
- }
-
- @Override
- Class[] getParamTypes() {
- return paramTypes;
- }
-
- @Override
- String getName() {
- return member.getName();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/ResourceBundleModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ResourceBundleModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ResourceBundleModel.java
deleted file mode 100644
index a79e786..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ResourceBundleModel.java
+++ /dev/null
@@ -1,190 +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.text.MessageFormat;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-import java.util.Set;
-
-import org.apache.freemarker.core._DelayedJQuote;
-import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * <p>A hash model that wraps a resource bundle. Makes it convenient to store
- * localized content in the data model. It also acts as a method model that will
- * take a resource key and arbitrary number of arguments and will apply
- * {@link MessageFormat} with arguments on the string represented by the key.</p>
- *
- * <p>Typical usages:</p>
- * <ul>
- * <li><tt>bundle.resourceKey</tt> will retrieve the object from resource bundle
- * with key <tt>resourceKey</tt></li>
- * <li><tt>bundle("patternKey", arg1, arg2, arg3)</tt> will retrieve the string
- * from resource bundle with key <tt>patternKey</tt>, and will use it as a pattern
- * for MessageFormat with arguments arg1, arg2 and arg3</li>
- * </ul>
- */
-public class ResourceBundleModel
- extends
- BeanModel
- implements
- TemplateMethodModelEx {
- static final ModelFactory FACTORY =
- new ModelFactory()
- {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new ResourceBundleModel((ResourceBundle) object, (BeansWrapper) wrapper);
- }
- };
-
- private Hashtable formats = null;
-
- public ResourceBundleModel(ResourceBundle bundle, BeansWrapper wrapper) {
- super(bundle, wrapper);
- }
-
- /**
- * Overridden to invoke the getObject method of the resource bundle.
- */
- @Override
- protected TemplateModel invokeGenericGet(Map keyMap, Class clazz, String key)
- throws TemplateModelException {
- try {
- return wrap(((ResourceBundle) object).getObject(key));
- } catch (MissingResourceException e) {
- throw new _TemplateModelException(e,
- "No ", new _DelayedJQuote(key), " key in the ResourceBundle. "
- + "Note that conforming to the ResourceBundle Java API, this is an error and not just "
- + "a missing sub-variable (a null).");
- }
- }
-
- /**
- * Returns true if this bundle contains no objects.
- */
- @Override
- public boolean isEmpty() {
- return !((ResourceBundle) object).getKeys().hasMoreElements() &&
- super.isEmpty();
- }
-
- @Override
- public int size() {
- return keySet().size();
- }
-
- @Override
- protected Set keySet() {
- Set set = super.keySet();
- Enumeration e = ((ResourceBundle) object).getKeys();
- while (e.hasMoreElements()) {
- set.add(e.nextElement());
- }
- return set;
- }
-
- /**
- * Takes first argument as a resource key, looks up a string in resource bundle
- * with this key, then applies a MessageFormat.format on the string with the
- * rest of the arguments. The created MessageFormats are cached for later reuse.
- */
- @Override
- public Object exec(List arguments)
- throws TemplateModelException {
- // Must have at least one argument - the key
- if (arguments.size() < 1)
- throw new TemplateModelException("No message key was specified");
- // Read it
- Iterator it = arguments.iterator();
- String key = unwrap((TemplateModel) it.next()).toString();
- try {
- if (!it.hasNext()) {
- return wrap(((ResourceBundle) object).getObject(key));
- }
-
- // Copy remaining arguments into an Object[]
- int args = arguments.size() - 1;
- Object[] params = new Object[args];
- for (int i = 0; i < args; ++i)
- params[i] = unwrap((TemplateModel) it.next());
-
- // Invoke format
- return new StringModel(format(key, params), wrapper);
- } catch (MissingResourceException e) {
- throw new TemplateModelException("No such key: " + key);
- } catch (Exception e) {
- throw new TemplateModelException(e.getMessage());
- }
- }
-
- /**
- * Provides direct access to caching format engine from code (instead of from script).
- */
- public String format(String key, Object[] params)
- throws MissingResourceException {
- // Check to see if we already have a cache for message formats
- // and construct it if we don't
- // NOTE: this block statement should be synchronized. However
- // concurrent creation of two caches will have no harmful
- // consequences, and we avoid a performance hit.
- /* synchronized(this) */
- {
- if (formats == null)
- formats = new Hashtable();
- }
-
- MessageFormat format = null;
- // Check to see if we already have a requested MessageFormat cached
- // and construct it if we don't
- // NOTE: this block statement should be synchronized. However
- // concurrent creation of two formats will have no harmful
- // consequences, and we avoid a performance hit.
- /* synchronized(formats) */
- {
- format = (MessageFormat) formats.get(key);
- if (format == null) {
- format = new MessageFormat(((ResourceBundle) object).getString(key));
- format.setLocale(getBundle().getLocale());
- formats.put(key, format);
- }
- }
-
- // Perform the formatting. We synchronize on it in case it
- // contains date formatting, which is not thread-safe.
- synchronized (format) {
- return format.format(params);
- }
- }
-
- public ResourceBundle getBundle() {
- return (ResourceBundle) object;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/SequenceAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/SequenceAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/SequenceAdapter.java
deleted file mode 100644
index 7f66cf9..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/SequenceAdapter.java
+++ /dev/null
@@ -1,68 +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.util.AbstractList;
-
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelAdapter;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.util.UndeclaredThrowableException;
-
-/**
- */
-class SequenceAdapter extends AbstractList implements TemplateModelAdapter {
- private final BeansWrapper wrapper;
- private final TemplateSequenceModel model;
-
- SequenceAdapter(TemplateSequenceModel model, BeansWrapper wrapper) {
- this.model = model;
- this.wrapper = wrapper;
- }
-
- @Override
- public TemplateModel getTemplateModel() {
- return model;
- }
-
- @Override
- public int size() {
- try {
- return model.size();
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public Object get(int index) {
- try {
- return wrapper.unwrap(model.get(index));
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- public TemplateSequenceModel getTemplateSequenceModel() {
- return model;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/SetAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/SetAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/SetAdapter.java
deleted file mode 100644
index 2d65efa..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/SetAdapter.java
+++ /dev/null
@@ -1,32 +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.util.Set;
-
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-
-/**
- */
-class SetAdapter extends CollectionAdapter implements Set {
- SetAdapter(TemplateCollectionModel model, BeansWrapper wrapper) {
- super(model, wrapper);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMapModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMapModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMapModel.java
deleted file mode 100644
index 80e04f7..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMapModel.java
+++ /dev/null
@@ -1,132 +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.util.List;
-import java.util.Map;
-
-import org.apache.freemarker.core.model.AdapterTemplateModel;
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.RichObjectWrapper;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx2;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
-import org.apache.freemarker.core.model.WrapperTemplateModel;
-import org.apache.freemarker.core.model.WrappingTemplateModel;
-import org.apache.freemarker.core.model.impl.CollectionAndSequence;
-import org.apache.freemarker.core.model.impl.MapKeyValuePairIterator;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
-
-/**
- * Model used by {@link BeansWrapper} when <tt>simpleMapWrapper</tt>
- * mode is enabled. Provides a simple hash model interface to the
- * underlying map (does not copy like {@link org.apache.freemarker.core.model.impl.SimpleHash}),
- * and a method interface to non-string keys.
- */
-public class SimpleMapModel extends WrappingTemplateModel
-implements TemplateHashModelEx2, TemplateMethodModelEx, AdapterTemplateModel,
-WrapperTemplateModel, TemplateModelWithAPISupport {
- static final ModelFactory FACTORY =
- new ModelFactory()
- {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new SimpleMapModel((Map) object, (BeansWrapper) wrapper);
- }
- };
-
- private final Map map;
-
- public SimpleMapModel(Map map, BeansWrapper wrapper) {
- super(wrapper);
- this.map = map;
- }
-
- @Override
- public TemplateModel get(String key) throws TemplateModelException {
- Object val = map.get(key);
- if (val == null) {
- if (key.length() == 1) {
- // just check for Character key if this is a single-character string
- Character charKey = Character.valueOf(key.charAt(0));
- val = map.get(charKey);
- if (val == null && !(map.containsKey(key) || map.containsKey(charKey))) {
- return null;
- }
- } else if (!map.containsKey(key)) {
- return null;
- }
- }
- return wrap(val);
- }
-
- @Override
- public Object exec(List args) throws TemplateModelException {
- Object key = ((BeansWrapper) getObjectWrapper()).unwrap((TemplateModel) args.get(0));
- Object value = map.get(key);
- if (value == null && !map.containsKey(key)) {
- return null;
- }
- return wrap(value);
- }
-
- @Override
- public boolean isEmpty() {
- return map.isEmpty();
- }
-
- @Override
- public int size() {
- return map.size();
- }
-
- @Override
- public TemplateCollectionModel keys() {
- return new CollectionAndSequence(new SimpleSequence(map.keySet(), getObjectWrapper()));
- }
-
- @Override
- public TemplateCollectionModel values() {
- return new CollectionAndSequence(new SimpleSequence(map.values(), getObjectWrapper()));
- }
-
- @Override
- public KeyValuePairIterator keyValuePairIterator() {
- return new MapKeyValuePairIterator(map, getObjectWrapper());
- }
-
- @Override
- public Object getAdaptedObject(Class hint) {
- return map;
- }
-
- @Override
- public Object getWrappedObject() {
- return map;
- }
-
- @Override
- public TemplateModel getAPI() throws TemplateModelException {
- return ((RichObjectWrapper) getObjectWrapper()).wrapAsAPI(map);
- }
-}
\ 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/SimpleMethod.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMethod.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMethod.java
deleted file mode 100644
index f863486..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMethod.java
+++ /dev/null
@@ -1,174 +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.Array;
-import java.lang.reflect.Member;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.freemarker.core._DelayedFTLTypeDescription;
-import org.apache.freemarker.core._DelayedOrdinal;
-import org.apache.freemarker.core._ErrorDescriptionBuilder;
-import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
-import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.util._ClassUtil;
-
-/**
- * This class is used for as a base for non-overloaded method models and for constructors.
- * (For overloaded methods and constructors see {@link OverloadedMethods}.)
- */
-class SimpleMethod {
-
- static final String MARKUP_OUTPUT_TO_STRING_TIP
- = "A markup output value can be converted to markup string like value?markup_string. "
- + "But consider if the Java method whose argument it will be can handle markup strings properly.";
-
- private final Member member;
- private final Class[] argTypes;
-
- protected SimpleMethod(Member member, Class[] argTypes) {
- this.member = member;
- this.argTypes = argTypes;
- }
-
- Object[] unwrapArguments(List arguments, BeansWrapper wrapper) throws TemplateModelException {
- if (arguments == null) {
- arguments = Collections.EMPTY_LIST;
- }
- boolean isVarArg = _MethodUtil.isVarargs(member);
- int typesLen = argTypes.length;
- if (isVarArg) {
- if (typesLen - 1 > arguments.size()) {
- throw new _TemplateModelException(
- _MethodUtil.invocationErrorMessageStart(member),
- " takes at least ", Integer.valueOf(typesLen - 1),
- typesLen - 1 == 1 ? " argument" : " arguments", ", but ",
- Integer.valueOf(arguments.size()), " was given.");
- }
- } else if (typesLen != arguments.size()) {
- throw new _TemplateModelException(
- _MethodUtil.invocationErrorMessageStart(member),
- " takes ", Integer.valueOf(typesLen), typesLen == 1 ? " argument" : " arguments", ", but ",
- Integer.valueOf(arguments.size()), " was given.");
- }
-
- return unwrapArguments(arguments, argTypes, isVarArg, wrapper);
- }
-
- private Object[] unwrapArguments(List args, Class[] argTypes, boolean isVarargs,
- BeansWrapper w)
- throws TemplateModelException {
- if (args == null) return null;
-
- int typesLen = argTypes.length;
- int argsLen = args.size();
-
- Object[] unwrappedArgs = new Object[typesLen];
-
- // Unwrap arguments:
- Iterator it = args.iterator();
- int normalArgCnt = isVarargs ? typesLen - 1 : typesLen;
- int argIdx = 0;
- while (argIdx < normalArgCnt) {
- Class argType = argTypes[argIdx];
- TemplateModel argVal = (TemplateModel) it.next();
- Object unwrappedArgVal = w.tryUnwrapTo(argVal, argType);
- if (unwrappedArgVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
- throw createArgumentTypeMismarchException(argIdx, argVal, argType);
- }
- if (unwrappedArgVal == null && argType.isPrimitive()) {
- throw createNullToPrimitiveArgumentException(argIdx, argType);
- }
-
- unwrappedArgs[argIdx++] = unwrappedArgVal;
- }
- if (isVarargs) {
- // The last argType, which is the vararg type, wasn't processed yet.
-
- Class varargType = argTypes[typesLen - 1];
- Class varargItemType = varargType.getComponentType();
- if (!it.hasNext()) {
- unwrappedArgs[argIdx++] = Array.newInstance(varargItemType, 0);
- } else {
- TemplateModel argVal = (TemplateModel) it.next();
-
- Object unwrappedArgVal;
- // We first try to treat the last argument as a vararg *array*.
- // This is consistent to what OverloadedVarArgMethod does.
- if (argsLen - argIdx == 1
- && (unwrappedArgVal = w.tryUnwrapTo(argVal, varargType))
- != ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
- // It was a vararg array.
- unwrappedArgs[argIdx++] = unwrappedArgVal;
- } else {
- // It wasn't a vararg array, so we assume it's a vararg
- // array *item*, possibly followed by further ones.
- int varargArrayLen = argsLen - argIdx;
- Object varargArray = Array.newInstance(varargItemType, varargArrayLen);
- for (int varargIdx = 0; varargIdx < varargArrayLen; varargIdx++) {
- TemplateModel varargVal = (TemplateModel) (varargIdx == 0 ? argVal : it.next());
- Object unwrappedVarargVal = w.tryUnwrapTo(varargVal, varargItemType);
- if (unwrappedVarargVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
- throw createArgumentTypeMismarchException(
- argIdx + varargIdx, varargVal, varargItemType);
- }
-
- if (unwrappedVarargVal == null && varargItemType.isPrimitive()) {
- throw createNullToPrimitiveArgumentException(argIdx + varargIdx, varargItemType);
- }
- Array.set(varargArray, varargIdx, unwrappedVarargVal);
- }
- unwrappedArgs[argIdx++] = varargArray;
- }
- }
- }
-
- return unwrappedArgs;
- }
-
- private TemplateModelException createArgumentTypeMismarchException(
- int argIdx, TemplateModel argVal, Class targetType) {
- _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
- _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: Can't convert the ",
- new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
- " argument's value to the target Java type, ", _ClassUtil.getShortClassName(targetType),
- ". The type of the actual value was: ", new _DelayedFTLTypeDescription(argVal));
- if (argVal instanceof TemplateMarkupOutputModel && (targetType.isAssignableFrom(String.class))) {
- desc.tip(MARKUP_OUTPUT_TO_STRING_TIP);
- }
- return new _TemplateModelException(desc);
- }
-
- private TemplateModelException createNullToPrimitiveArgumentException(int argIdx, Class targetType) {
- return new _TemplateModelException(
- _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: The value of the ",
- new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
- " argument was null, but the target Java parameter type (", _ClassUtil.getShortClassName(targetType),
- ") is primitive and so can't store null.");
- }
-
- protected Member getMember() {
- return member;
- }
-}
\ 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/SimpleMethodModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMethodModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMethodModel.java
deleted file mode 100644
index 32c2bc2..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/SimpleMethodModel.java
+++ /dev/null
@@ -1,134 +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.Member;
-import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-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.impl.SimpleNumber;
-import org.apache.freemarker.core.util.FTLUtil;
-
-/**
- * A class that will wrap a reflected method call into a
- * {@link org.apache.freemarker.core.model.TemplateMethodModel} interface.
- * It is used by {@link BeanModel} to wrap reflected method calls
- * for non-overloaded methods.
- */
-public final class SimpleMethodModel extends SimpleMethod
- implements
- TemplateMethodModelEx,
- TemplateSequenceModel,
- _UnexpectedTypeErrorExplainerTemplateModel {
- private final Object object;
- private final BeansWrapper wrapper;
-
- /**
- * Creates a model for a specific method on a specific object.
- * @param object the object to call the method on, or {@code null} for a static method.
- * @param method the method that will be invoked.
- * @param argTypes Either pass in {@code Method#getParameterTypes() method.getParameterTypes()} here,
- * or reuse an earlier result of that call (for speed). Not {@code null}.
- */
- SimpleMethodModel(Object object, Method method, Class[] argTypes,
- BeansWrapper wrapper) {
- super(method, argTypes);
- this.object = object;
- this.wrapper = wrapper;
- }
-
- /**
- * Invokes the method, passing it the arguments from the list.
- */
- @Override
- public Object exec(List arguments)
- throws TemplateModelException {
- try {
- return wrapper.invokeMethod(object, (Method) getMember(),
- unwrapArguments(arguments, wrapper));
- } catch (TemplateModelException e) {
- throw e;
- } catch (Exception e) {
- throw _MethodUtil.newInvocationTemplateModelException(object, getMember(), e);
- }
- }
-
- @Override
- public TemplateModel get(int index) throws TemplateModelException {
- return (TemplateModel) exec(Collections.singletonList(
- new SimpleNumber(Integer.valueOf(index))));
- }
-
- @Override
- public int size() throws TemplateModelException {
- throw new TemplateModelException(
- "Getting the number of items or enumerating the items is not supported on this "
- + FTLUtil.getTypeDescription(this) + " value.\n"
- + "("
- + "Hint 1: Maybe you wanted to call this method first and then do something with its return value. "
- + "Hint 2: Getting items by intex possibly works, hence it's a \"+sequence\"."
- + ")");
- }
-
- @Override
- public String toString() {
- return getMember().toString();
- }
-
- /**
- * Implementation of experimental interface; don't use it, no backward compatibility guarantee!
- */
- @Override
- public Object[] explainTypeError(Class[] expectedClasses) {
- final Member member = getMember();
- if (!(member instanceof Method)) {
- return null; // This shouldn't occur
- }
- Method m = (Method) member;
-
- final Class returnType = m.getReturnType();
- if (returnType == null || returnType == void.class || returnType == Void.class) {
- return null; // Calling it won't help
- }
-
- String mName = m.getName();
- if (mName.startsWith("get") && mName.length() > 3 && Character.isUpperCase(mName.charAt(3))
- && (m.getParameterTypes().length == 0)) {
- return new Object[] {
- "Maybe using obj.something instead of obj.getSomething will yield the desired value." };
- } else if (mName.startsWith("is") && mName.length() > 2 && Character.isUpperCase(mName.charAt(2))
- && (m.getParameterTypes().length == 0)) {
- return new Object[] {
- "Maybe using obj.something instead of obj.isSomething will yield the desired value." };
- } else {
- return new Object[] {
- "Maybe using obj.something(",
- (m.getParameterTypes().length != 0 ? "params" : ""),
- ") instead of obj.something will yield the desired value" };
- }
- }
-
-}
\ 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/SingletonCustomizer.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/SingletonCustomizer.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/SingletonCustomizer.java
deleted file mode 100644
index df2a6c3..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/SingletonCustomizer.java
+++ /dev/null
@@ -1,51 +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 org.apache.freemarker.core.model.ObjectWrapper;
-
-/**
- * Marker interface useful when used together with {@link MethodAppearanceFineTuner} and such customizer objects, to
- * indicate that it <b>doesn't contain reference to the {@link ObjectWrapper}</b> (so beware with non-static inner
- * classes) and can be and should be used in call introspection cache keys. This also implies that you won't
- * create many instances of the class, rather just reuse the same (or same few) instances over and over. Furthermore,
- * the instances must be thread-safe. The typical pattern in which this instance should be used is like this:
- *
- * <pre>static class MyMethodAppearanceFineTuner implements MethodAppearanceFineTuner, SingletonCustomizer {
- *
- * // This is the singleton:
- * static final MyMethodAppearanceFineTuner INSTANCE = new MyMethodAppearanceFineTuner();
- *
- * // Private, so it can't be constructed from outside this class.
- * private MyMethodAppearanceFineTuner() { }
- *
- * @Override
- * public void fineTuneMethodAppearance(...) {
- * // Do something here, only using the parameters and maybe some other singletons.
- * ...
- * }
- *
- * }</pre>
- *
- * @since 2.3.21
- */
-public interface SingletonCustomizer {
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModel.java
deleted file mode 100644
index a302d8b..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModel.java
+++ /dev/null
@@ -1,177 +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.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.slf4j.Logger;
-
-/**
- * Wraps the static fields and methods of a class in a
- * {@link org.apache.freemarker.core.model.TemplateHashModel}.
- * Fields are wrapped using {@link BeansWrapper#wrap(Object)}, and
- * methods are wrapped into an appropriate {@link org.apache.freemarker.core.model.TemplateMethodModelEx} instance.
- * Unfortunately, there is currently no support for bean property-style
- * calls of static methods, similar to that in {@link BeanModel}.
- */
-final class StaticModel implements TemplateHashModelEx {
-
- private static final Logger LOG = _CoreLogs.BEANS_WRAPPER;
-
- private final Class clazz;
- private final BeansWrapper wrapper;
- private final Map map = new HashMap();
-
- StaticModel(Class clazz, BeansWrapper wrapper) throws TemplateModelException {
- this.clazz = clazz;
- this.wrapper = wrapper;
- populate();
- }
-
- /**
- * Returns the field or method named by the <tt>key</tt>
- * parameter.
- */
- @Override
- public TemplateModel get(String key) throws TemplateModelException {
- Object model = map.get(key);
- // Simple method, overloaded method or final field -- these have cached
- // template models
- if (model instanceof TemplateModel)
- return (TemplateModel) model;
- // Non-final field; this must be evaluated on each call.
- if (model instanceof Field) {
- try {
- return wrapper.getOuterIdentity().wrap(((Field) model).get(null));
- } catch (IllegalAccessException e) {
- throw new TemplateModelException(
- "Illegal access for field " + key + " of class " + clazz.getName());
- }
- }
-
- throw new TemplateModelException(
- "No such key: " + key + " in class " + clazz.getName());
- }
-
- /**
- * Returns true if there is at least one public static
- * field or method in the underlying class.
- */
- @Override
- public boolean isEmpty() {
- return map.isEmpty();
- }
-
- @Override
- public int size() {
- return map.size();
- }
-
- @Override
- public TemplateCollectionModel keys() throws TemplateModelException {
- return (TemplateCollectionModel) wrapper.getOuterIdentity().wrap(map.keySet());
- }
-
- @Override
- public TemplateCollectionModel values() throws TemplateModelException {
- return (TemplateCollectionModel) wrapper.getOuterIdentity().wrap(map.values());
- }
-
- private void populate() throws TemplateModelException {
- if (!Modifier.isPublic(clazz.getModifiers())) {
- throw new TemplateModelException(
- "Can't wrap the non-public class " + clazz.getName());
- }
-
- if (wrapper.getExposureLevel() == BeansWrapper.EXPOSE_NOTHING) {
- return;
- }
-
- Field[] fields = clazz.getFields();
- for (Field field : fields) {
- int mod = field.getModifiers();
- if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
- if (Modifier.isFinal(mod))
- try {
- // public static final fields are evaluated once and
- // stored in the map
- map.put(field.getName(), wrapper.getOuterIdentity().wrap(field.get(null)));
- } catch (IllegalAccessException e) {
- // Intentionally ignored
- }
- else
- // This is a special flagging value: Field in the map means
- // that this is a non-final field, and it must be evaluated
- // on each get() call.
- map.put(field.getName(), field);
- }
- }
- if (wrapper.getExposureLevel() < BeansWrapper.EXPOSE_PROPERTIES_ONLY) {
- Method[] methods = clazz.getMethods();
- for (Method method : methods) {
- int mod = method.getModifiers();
- if (Modifier.isPublic(mod) && Modifier.isStatic(mod)
- && wrapper.getClassIntrospector().isAllowedToExpose(method)) {
- String name = method.getName();
- Object obj = map.get(name);
- if (obj instanceof Method) {
- OverloadedMethods overloadedMethods = new OverloadedMethods();
- overloadedMethods.addMethod((Method) obj);
- overloadedMethods.addMethod(method);
- map.put(name, overloadedMethods);
- } else if (obj instanceof OverloadedMethods) {
- OverloadedMethods overloadedMethods = (OverloadedMethods) obj;
- overloadedMethods.addMethod(method);
- } else {
- if (obj != null) {
- if (LOG.isInfoEnabled()) {
- LOG.info("Overwriting value [" + obj + "] for " +
- " key '" + name + "' with [" + method +
- "] in static model for " + clazz.getName());
- }
- }
- map.put(name, method);
- }
- }
- }
- for (Iterator entries = map.entrySet().iterator(); entries.hasNext(); ) {
- Map.Entry entry = (Map.Entry) entries.next();
- Object value = entry.getValue();
- if (value instanceof Method) {
- Method method = (Method) value;
- entry.setValue(new SimpleMethodModel(null, method,
- method.getParameterTypes(), wrapper));
- } else if (value instanceof OverloadedMethods) {
- entry.setValue(new OverloadedMethodsModel(null, (OverloadedMethods) value, wrapper));
- }
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModels.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModels.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModels.java
deleted file mode 100644
index f89fda8..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/StaticModels.java
+++ /dev/null
@@ -1,43 +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 org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * Utility class for instantiating {@link StaticModel} instances from
- * templates. If your template's data model contains an instance of
- * StaticModels (named, say <tt>StaticModels</tt>), then you can
- * instantiate an arbitrary StaticModel using get syntax (i.e.
- * <tt>StaticModels["java.lang.System"].currentTimeMillis()</tt>).
- */
-class StaticModels extends ClassBasedModelFactory {
-
- StaticModels(BeansWrapper wrapper) {
- super(wrapper);
- }
-
- @Override
- protected TemplateModel createModel(Class clazz)
- throws TemplateModelException {
- return new StaticModel(clazz, getWrapper());
- }
-}
\ 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/StringModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/StringModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/StringModel.java
deleted file mode 100644
index 0591f6e..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/StringModel.java
+++ /dev/null
@@ -1,63 +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 org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-
-/**
- * Subclass of {@link BeanModel} that exposes the return value of the {@link
- * java.lang.Object#toString()} method through the {@link TemplateScalarModel}
- * interface.
- */
-public class StringModel extends BeanModel
-implements TemplateScalarModel {
- static final ModelFactory FACTORY =
- new ModelFactory()
- {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new StringModel(object, (BeansWrapper) wrapper);
- }
- };
-
- /**
- * Creates a new model that wraps the specified object with BeanModel + scalar
- * functionality.
- * @param object the object to wrap into a model.
- * @param wrapper the {@link BeansWrapper} associated with this model.
- * Every model has to have an associated {@link BeansWrapper} instance. The
- * model gains many attributes from its wrapper, including the caching
- * behavior, method exposure level, method-over-item shadowing policy etc.
- */
- public StringModel(Object object, BeansWrapper wrapper) {
- super(object, wrapper);
- }
-
- /**
- * Returns the result of calling {@link Object#toString()} on the wrapped
- * object.
- */
- @Override
- public String getAsString() {
- return object.toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/TypeFlags.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/TypeFlags.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/TypeFlags.java
deleted file mode 100644
index e836e47..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/TypeFlags.java
+++ /dev/null
@@ -1,130 +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.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Flag values and masks for "type flags". "Type flags" is a set of bits that store information about the possible
- * destination types at a parameter position of overloaded methods.
- */
-class TypeFlags {
-
- /**
- * Indicates that the unwrapping hint will not be a specific numerical type; it must not be set if there's no
- * numerical type at the given parameter index.
- */
- static final int WIDENED_NUMERICAL_UNWRAPPING_HINT = 1;
-
- static final int BYTE = 4;
- static final int SHORT = 8;
- static final int INTEGER = 16;
- static final int LONG = 32;
- static final int FLOAT = 64;
- static final int DOUBLE = 128;
- static final int BIG_INTEGER = 256;
- static final int BIG_DECIMAL = 512;
- static final int UNKNOWN_NUMERICAL_TYPE = 1024;
-
- static final int ACCEPTS_NUMBER = 0x800;
- static final int ACCEPTS_DATE = 0x1000;
- static final int ACCEPTS_STRING = 0x2000;
- static final int ACCEPTS_BOOLEAN = 0x4000;
- static final int ACCEPTS_MAP = 0x8000;
- static final int ACCEPTS_LIST = 0x10000;
- static final int ACCEPTS_SET = 0x20000;
- static final int ACCEPTS_ARRAY = 0x40000;
-
- /**
- * Indicates the presence of the char or Character type
- */
- static final int CHARACTER = 0x80000;
-
- static final int ACCEPTS_ANY_OBJECT = ACCEPTS_NUMBER | ACCEPTS_DATE | ACCEPTS_STRING | ACCEPTS_BOOLEAN
- | ACCEPTS_MAP | ACCEPTS_LIST | ACCEPTS_SET | ACCEPTS_ARRAY;
-
- static final int MASK_KNOWN_INTEGERS = BYTE | SHORT | INTEGER | LONG | BIG_INTEGER;
- static final int MASK_KNOWN_NONINTEGERS = FLOAT | DOUBLE | BIG_DECIMAL;
- static final int MASK_ALL_KNOWN_NUMERICALS = MASK_KNOWN_INTEGERS | MASK_KNOWN_NONINTEGERS;
- static final int MASK_ALL_NUMERICALS = MASK_ALL_KNOWN_NUMERICALS | UNKNOWN_NUMERICAL_TYPE;
-
- static int classToTypeFlags(Class pClass) {
- // We start with the most frequent cases
- if (pClass == Object.class) {
- return ACCEPTS_ANY_OBJECT;
- } else if (pClass == String.class) {
- return ACCEPTS_STRING;
- } else if (pClass.isPrimitive()) {
- if (pClass == Integer.TYPE) return INTEGER | ACCEPTS_NUMBER;
- else if (pClass == Long.TYPE) return LONG | ACCEPTS_NUMBER;
- else if (pClass == Double.TYPE) return DOUBLE | ACCEPTS_NUMBER;
- else if (pClass == Float.TYPE) return FLOAT | ACCEPTS_NUMBER;
- else if (pClass == Byte.TYPE) return BYTE | ACCEPTS_NUMBER;
- else if (pClass == Short.TYPE) return SHORT | ACCEPTS_NUMBER;
- else if (pClass == Character.TYPE) return CHARACTER;
- else if (pClass == Boolean.TYPE) return ACCEPTS_BOOLEAN;
- else return 0;
- } else if (Number.class.isAssignableFrom(pClass)) {
- if (pClass == Integer.class) return INTEGER | ACCEPTS_NUMBER;
- else if (pClass == Long.class) return LONG | ACCEPTS_NUMBER;
- else if (pClass == Double.class) return DOUBLE | ACCEPTS_NUMBER;
- else if (pClass == Float.class) return FLOAT | ACCEPTS_NUMBER;
- else if (pClass == Byte.class) return BYTE | ACCEPTS_NUMBER;
- else if (pClass == Short.class) return SHORT | ACCEPTS_NUMBER;
- else if (BigDecimal.class.isAssignableFrom(pClass)) return BIG_DECIMAL | ACCEPTS_NUMBER;
- else if (BigInteger.class.isAssignableFrom(pClass)) return BIG_INTEGER | ACCEPTS_NUMBER;
- else return UNKNOWN_NUMERICAL_TYPE | ACCEPTS_NUMBER;
- } else if (pClass.isArray()) {
- return ACCEPTS_ARRAY;
- } else {
- int flags = 0;
- if (pClass.isAssignableFrom(String.class)) {
- flags |= ACCEPTS_STRING;
- }
- if (pClass.isAssignableFrom(Date.class)) {
- flags |= ACCEPTS_DATE;
- }
- if (pClass.isAssignableFrom(Boolean.class)) {
- flags |= ACCEPTS_BOOLEAN;
- }
- if (pClass.isAssignableFrom(Map.class)) {
- flags |= ACCEPTS_MAP;
- }
- if (pClass.isAssignableFrom(List.class)) {
- flags |= ACCEPTS_LIST;
- }
- if (pClass.isAssignableFrom(Set.class)) {
- flags |= ACCEPTS_SET;
- }
-
- if (pClass == Character.class) {
- flags |= CHARACTER;
- }
-
- return flags;
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/UnsafeMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/UnsafeMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/UnsafeMethods.java
deleted file mode 100644
index 93b7f3c..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/UnsafeMethods.java
+++ /dev/null
@@ -1,112 +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.io.InputStream;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.StringTokenizer;
-
-import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core.util._ClassUtil;
-import org.slf4j.Logger;
-
-class UnsafeMethods {
-
- private static final Logger LOG = _CoreLogs.BEANS_WRAPPER;
- private static final String UNSAFE_METHODS_PROPERTIES = "unsafeMethods.properties";
- private static final Set UNSAFE_METHODS = createUnsafeMethodsSet();
-
- private UnsafeMethods() { }
-
- static boolean isUnsafeMethod(Method method) {
- return UNSAFE_METHODS.contains(method);
- }
-
- private static Set createUnsafeMethodsSet() {
- Properties props = new Properties();
- InputStream in = BeansWrapper.class.getResourceAsStream(UNSAFE_METHODS_PROPERTIES);
- if (in == null) {
- throw new IllegalStateException("Class loader resource not found: "
- + BeansWrapper.class.getPackage().getName() + UNSAFE_METHODS_PROPERTIES);
- }
- String methodSpec = null;
- try {
- try {
- props.load(in);
- } finally {
- in.close();
- }
- Set set = new HashSet(props.size() * 4 / 3, 1f);
- Map primClasses = createPrimitiveClassesMap();
- for (Iterator iterator = props.keySet().iterator(); iterator.hasNext(); ) {
- methodSpec = (String) iterator.next();
- try {
- set.add(parseMethodSpec(methodSpec, primClasses));
- } catch (ClassNotFoundException | NoSuchMethodException e) {
- LOG.debug("Failed to get unsafe method", e);
- }
- }
- return set;
- } catch (Exception e) {
- throw new RuntimeException("Could not load unsafe method " + methodSpec + " " + e.getClass().getName() + " " + e.getMessage());
- }
- }
-
- private static Method parseMethodSpec(String methodSpec, Map primClasses)
- throws ClassNotFoundException,
- NoSuchMethodException {
- int brace = methodSpec.indexOf('(');
- int dot = methodSpec.lastIndexOf('.', brace);
- Class clazz = _ClassUtil.forName(methodSpec.substring(0, dot));
- String methodName = methodSpec.substring(dot + 1, brace);
- String argSpec = methodSpec.substring(brace + 1, methodSpec.length() - 1);
- StringTokenizer tok = new StringTokenizer(argSpec, ",");
- int argcount = tok.countTokens();
- Class[] argTypes = new Class[argcount];
- for (int i = 0; i < argcount; i++) {
- String argClassName = tok.nextToken();
- argTypes[i] = (Class) primClasses.get(argClassName);
- if (argTypes[i] == null) {
- argTypes[i] = _ClassUtil.forName(argClassName);
- }
- }
- return clazz.getMethod(methodName, argTypes);
- }
-
- private static Map createPrimitiveClassesMap() {
- Map map = new HashMap();
- map.put("boolean", Boolean.TYPE);
- map.put("byte", Byte.TYPE);
- map.put("char", Character.TYPE);
- map.put("short", Short.TYPE);
- map.put("int", Integer.TYPE);
- map.put("long", Long.TYPE);
- map.put("float", Float.TYPE);
- map.put("double", Double.TYPE);
- return map;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/_BeansAPI.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/_BeansAPI.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/_BeansAPI.java
deleted file mode 100644
index cac3ba7..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/_BeansAPI.java
+++ /dev/null
@@ -1,220 +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.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-import org.apache.freemarker.core.util.BugException;
-import org.apache.freemarker.core.util._CollectionUtil;
-
-/**
- * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
- * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can
- * access things inside this package that users shouldn't.
- */
-public class _BeansAPI {
-
- private _BeansAPI() { }
-
- public static Object newInstance(Class<?> pClass, Object[] args, BeansWrapper bw)
- throws NoSuchMethodException, IllegalArgumentException, InstantiationException,
- IllegalAccessException, InvocationTargetException, TemplateModelException {
- return newInstance(getConstructorDescriptor(pClass, args), args, bw);
- }
-
- /**
- * Gets the constructor that matches the types of the arguments the best. So this is more
- * than what the Java reflection API provides in that it can handle overloaded constructors. This re-uses the
- * overloaded method selection logic of {@link BeansWrapper}.
- */
- private static CallableMemberDescriptor getConstructorDescriptor(Class<?> pClass, Object[] args)
- throws NoSuchMethodException {
- if (args == null) args = _CollectionUtil.EMPTY_OBJECT_ARRAY;
-
- final ArgumentTypes argTypes = new ArgumentTypes(args);
- final List<ReflectionCallableMemberDescriptor> fixedArgMemberDescs
- = new ArrayList<>();
- final List<ReflectionCallableMemberDescriptor> varArgsMemberDescs
- = new ArrayList<>();
- for (Constructor<?> constr : pClass.getConstructors()) {
- ReflectionCallableMemberDescriptor memberDesc = new ReflectionCallableMemberDescriptor(constr, constr.getParameterTypes());
- if (!_MethodUtil.isVarargs(constr)) {
- fixedArgMemberDescs.add(memberDesc);
- } else {
- varArgsMemberDescs.add(memberDesc);
- }
- }
-
- MaybeEmptyCallableMemberDescriptor contrDesc = argTypes.getMostSpecific(fixedArgMemberDescs, false);
- if (contrDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) {
- contrDesc = argTypes.getMostSpecific(varArgsMemberDescs, true);
- }
-
- if (contrDesc instanceof EmptyCallableMemberDescriptor) {
- if (contrDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) {
- throw new NoSuchMethodException(
- "There's no public " + pClass.getName()
- + " constructor with compatible parameter list.");
- } else if (contrDesc == EmptyCallableMemberDescriptor.AMBIGUOUS_METHOD) {
- throw new NoSuchMethodException(
- "There are multiple public " + pClass.getName()
- + " constructors that match the compatible parameter list with the same preferability.");
- } else {
- throw new NoSuchMethodException();
- }
- } else {
- return (CallableMemberDescriptor) contrDesc;
- }
- }
-
- private static Object newInstance(CallableMemberDescriptor constrDesc, Object[] args, BeansWrapper bw)
- throws InstantiationException, IllegalAccessException, InvocationTargetException, IllegalArgumentException,
- TemplateModelException {
- if (args == null) args = _CollectionUtil.EMPTY_OBJECT_ARRAY;
-
- final Object[] packedArgs;
- if (constrDesc.isVarargs()) {
- // We have to put all the varargs arguments into a single array argument.
-
- final Class<?>[] paramTypes = constrDesc.getParamTypes();
- final int fixedArgCnt = paramTypes.length - 1;
-
- packedArgs = new Object[fixedArgCnt + 1];
- for (int i = 0; i < fixedArgCnt; i++) {
- packedArgs[i] = args[i];
- }
-
- final Class<?> compType = paramTypes[fixedArgCnt].getComponentType();
- final int varArgCnt = args.length - fixedArgCnt;
- final Object varArgsArray = Array.newInstance(compType, varArgCnt);
- for (int i = 0; i < varArgCnt; i++) {
- Array.set(varArgsArray, i, args[fixedArgCnt + i]);
- }
- packedArgs[fixedArgCnt] = varArgsArray;
- } else {
- packedArgs = args;
- }
-
- return constrDesc.invokeConstructor(bw, packedArgs);
- }
-
- /**
- * Contains the common parts of the singleton management for {@link BeansWrapper} and {@link DefaultObjectWrapper}.
- *
- * @param beansWrapperSubclassFactory Creates a <em>new</em> read-only object wrapper of the desired
- * {@link BeansWrapper} subclass.
- */
- public static <BW extends BeansWrapper, BWC extends BeansWrapperConfiguration> BW getBeansWrapperSubclassSingleton(
- BWC settings,
- Map<ClassLoader, Map<BWC, WeakReference<BW>>> instanceCache,
- ReferenceQueue<BW> instanceCacheRefQue,
- _BeansWrapperSubclassFactory<BW, BWC> beansWrapperSubclassFactory) {
- // BeansWrapper can't be cached across different Thread Context Class Loaders (TCCL), because the result of
- // a class name (String) to Class mappings depends on it, and the staticModels and enumModels need that.
- // (The ClassIntrospector doesn't have to consider the TCCL, as it only works with Class-es, not class
- // names.)
- ClassLoader tccl = Thread.currentThread().getContextClassLoader();
-
- Reference<BW> instanceRef;
- Map<BWC, WeakReference<BW>> tcclScopedCache;
- synchronized (instanceCache) {
- tcclScopedCache = instanceCache.get(tccl);
- if (tcclScopedCache == null) {
- tcclScopedCache = new HashMap<>();
- instanceCache.put(tccl, tcclScopedCache);
- instanceRef = null;
- } else {
- instanceRef = tcclScopedCache.get(settings);
- }
- }
-
- BW instance = instanceRef != null ? instanceRef.get() : null;
- if (instance != null) { // cache hit
- return instance;
- }
- // cache miss
-
- settings = clone(settings); // prevent any aliasing issues
- instance = beansWrapperSubclassFactory.create(settings);
- if (!instance.isWriteProtected()) {
- throw new BugException();
- }
-
- synchronized (instanceCache) {
- instanceRef = tcclScopedCache.get(settings);
- BW concurrentInstance = instanceRef != null ? instanceRef.get() : null;
- if (concurrentInstance == null) {
- tcclScopedCache.put(settings, new WeakReference<>(instance, instanceCacheRefQue));
- } else {
- instance = concurrentInstance;
- }
- }
-
- removeClearedReferencesFromCache(instanceCache, instanceCacheRefQue);
-
- return instance;
- }
-
- @SuppressWarnings("unchecked")
- private static <BWC extends BeansWrapperConfiguration> BWC clone(BWC settings) {
- return (BWC) settings.clone(true);
- }
-
- private static <BW extends BeansWrapper, BWC extends BeansWrapperConfiguration>
- void removeClearedReferencesFromCache(
- Map<ClassLoader, Map<BWC, WeakReference<BW>>> instanceCache,
- ReferenceQueue<BW> instanceCacheRefQue) {
- Reference<? extends BW> clearedRef;
- while ((clearedRef = instanceCacheRefQue.poll()) != null) {
- synchronized (instanceCache) {
- findClearedRef: for (Map<BWC, WeakReference<BW>> tcclScopedCache : instanceCache.values()) {
- for (Iterator<WeakReference<BW>> it2 = tcclScopedCache.values().iterator(); it2.hasNext(); ) {
- if (it2.next() == clearedRef) {
- it2.remove();
- break findClearedRef;
- }
- }
- }
- } // sync
- } // while poll
- }
-
- /**
- * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
- */
- public interface _BeansWrapperSubclassFactory<BW extends BeansWrapper, BWC extends BeansWrapperConfiguration> {
-
- /** Creates a new read-only {@link BeansWrapper}; used for {@link BeansWrapperBuilder} and such. */
- BW create(BWC sa);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/_EnumModels.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/_EnumModels.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/_EnumModels.java
deleted file mode 100644
index 573757b..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/_EnumModels.java
+++ /dev/null
@@ -1,54 +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.util.LinkedHashMap;
-import java.util.Map;
-
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Don't use this class; it's only public to work around Google App Engine Java
- * compliance issues. FreeMarker developers only: treat this class as package-visible.
- */
-public class _EnumModels extends ClassBasedModelFactory {
-
- public _EnumModels(BeansWrapper wrapper) {
- super(wrapper);
- }
-
- @Override
- protected TemplateModel createModel(Class clazz) {
- Object[] obj = clazz.getEnumConstants();
- if (obj == null) {
- // Return null - it'll manifest itself as undefined in the template.
- // We're doing this rather than throw an exception as this way
- // people can use someEnumModel?default({}) to gracefully fall back
- // to an empty hash if they want to.
- return null;
- }
- Map map = new LinkedHashMap();
- for (Object anObj : obj) {
- Enum value = (Enum) anObj;
- map.put(value.name(), value);
- }
- return new SimpleMapModel(map, getWrapper());
- }
-}
[15/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
new file mode 100644
index 0000000..a34c390
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedVarArgsMethods.java
@@ -0,0 +1,245 @@
+/*
+ * 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.lang.reflect.Array;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+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.util.BugException;
+
+/**
+ * Stores the varargs methods for a {@link OverloadedMethods} object.
+ */
+class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
+
+ OverloadedVarArgsMethods() {
+ super();
+ }
+
+ /**
+ * Replaces the last parameter type with the array component type of it.
+ */
+ @Override
+ Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) {
+ final Class[] preprocessedParamTypes = memberDesc.getParamTypes().clone();
+ int ln = preprocessedParamTypes.length;
+ final Class varArgsCompType = preprocessedParamTypes[ln - 1].getComponentType();
+ if (varArgsCompType == null) {
+ throw new BugException("Only varargs methods should be handled here");
+ }
+ preprocessedParamTypes[ln - 1] = varArgsCompType;
+ return preprocessedParamTypes;
+ }
+
+ @Override
+ void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes) {
+ // Overview
+ // --------
+ //
+ // So far, m(t1, t2...) was treated by the hint widening like m(t1, t2). So now we have to continue hint
+ // widening like if we had further methods:
+ // - m(t1, t2, t2), m(t1, t2, t2, t2), ...
+ // - m(t1), because a varargs array can be 0 long
+ //
+ // But we can't do that for real, because we had to add infinite number of methods. Also, for efficiency we
+ // don't want to create unwrappingHintsByParamCount entries at the indices which are still unused.
+ // So we only update the already existing hints. Remember that we already have m(t1, t2) there.
+
+ final int paramCount = paramTypes.length;
+ final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
+
+ // The case of e(t1, t2), e(t1, t2, t2), e(t1, t2, t2, t2), ..., where e is an *earlierly* added method.
+ // When that was added, this method wasn't added yet, so it had no chance updating the hints of this method,
+ // so we do that now:
+ // FIXME: Only needed if m(t1, t2) was filled an empty slot, otherwise whatever was there was already
+ // widened by the preceding hints, so this will be a no-op.
+ for (int i = paramCount - 1; i >= 0; i--) {
+ final Class[] previousHints = unwrappingHintsByParamCount[i];
+ if (previousHints != null) {
+ widenHintsToCommonSupertypes(
+ paramCount,
+ previousHints, getTypeFlags(i));
+ break; // we only do this for the first hit, as the methods before that has already widened it.
+ }
+ }
+ // The case of e(t1), where e is an *earlier* added method.
+ // When that was added, this method wasn't added yet, so it had no chance updating the hints of this method,
+ // so we do that now:
+ // FIXME: Same as above; it's often unnecessary.
+ if (paramCount + 1 < unwrappingHintsByParamCount.length) {
+ Class[] oneLongerHints = unwrappingHintsByParamCount[paramCount + 1];
+ if (oneLongerHints != null) {
+ widenHintsToCommonSupertypes(
+ paramCount,
+ oneLongerHints, getTypeFlags(paramCount + 1));
+ }
+ }
+
+ // The case of m(t1, t2, t2), m(t1, t2, t2, t2), ..., where m is the currently added method.
+ // Update the longer hints-arrays:
+ for (int i = paramCount + 1; i < unwrappingHintsByParamCount.length; i++) {
+ widenHintsToCommonSupertypes(
+ i,
+ paramTypes, paramNumericalTypes);
+ }
+ // The case of m(t1) where m is the currently added method.
+ // update the one-shorter hints-array:
+ if (paramCount > 0) { // (should be always true, or else it wasn't a varags method)
+ widenHintsToCommonSupertypes(
+ paramCount - 1,
+ paramTypes, paramNumericalTypes);
+ }
+
+ }
+
+ private void widenHintsToCommonSupertypes(
+ int paramCountOfWidened, Class[] wideningTypes, int[] wideningTypeFlags) {
+ final Class[] typesToWiden = getUnwrappingHintsByParamCount()[paramCountOfWidened];
+ if (typesToWiden == null) {
+ return; // no such overload exists; nothing to widen
+ }
+
+ final int typesToWidenLen = typesToWiden.length;
+ final int wideningTypesLen = wideningTypes.length;
+ int min = Math.min(wideningTypesLen, typesToWidenLen);
+ for (int i = 0; i < min; ++i) {
+ typesToWiden[i] = getCommonSupertypeForUnwrappingHint(typesToWiden[i], wideningTypes[i]);
+ }
+ if (typesToWidenLen > wideningTypesLen) {
+ Class varargsComponentType = wideningTypes[wideningTypesLen - 1];
+ for (int i = wideningTypesLen; i < typesToWidenLen; ++i) {
+ typesToWiden[i] = getCommonSupertypeForUnwrappingHint(typesToWiden[i], varargsComponentType);
+ }
+ }
+
+ mergeInTypesFlags(paramCountOfWidened, wideningTypeFlags);
+ }
+
+ @Override
+ MaybeEmptyMemberAndArguments getMemberAndArguments(List tmArgs, DefaultObjectWrapper unwrapper)
+ throws TemplateModelException {
+ if (tmArgs == null) {
+ // null is treated as empty args
+ tmArgs = Collections.EMPTY_LIST;
+ }
+ final int argsLen = tmArgs.size();
+ final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
+ final Object[] pojoArgs = new Object[argsLen];
+ int[] typesFlags = null;
+ // Going down starting from methods with args.length + 1 parameters, because we must try to match against a case
+ // where all specified args are fixargs, and we have 0 varargs.
+ outer: for (int paramCount = Math.min(argsLen + 1, unwrappingHintsByParamCount.length - 1); paramCount >= 0; --paramCount) {
+ Class[] unwarappingHints = unwrappingHintsByParamCount[paramCount];
+ if (unwarappingHints == null) {
+ if (paramCount == 0) {
+ return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS;
+ }
+ continue;
+ }
+
+ typesFlags = getTypeFlags(paramCount);
+ if (typesFlags == ALL_ZEROS_ARRAY) {
+ typesFlags = null;
+ }
+
+ // Try to unwrap the arguments
+ Iterator it = tmArgs.iterator();
+ for (int i = 0; i < argsLen; ++i) {
+ int paramIdx = i < paramCount ? i : paramCount - 1;
+ Object pojo = unwrapper.tryUnwrapTo(
+ (TemplateModel) it.next(),
+ unwarappingHints[paramIdx],
+ typesFlags != null ? typesFlags[paramIdx] : 0);
+ if (pojo == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ continue outer;
+ }
+ pojoArgs[i] = pojo;
+ }
+ break outer;
+ }
+
+ MaybeEmptyCallableMemberDescriptor maybeEmtpyMemberDesc = getMemberDescriptorForArgs(pojoArgs, true);
+ if (maybeEmtpyMemberDesc instanceof CallableMemberDescriptor) {
+ CallableMemberDescriptor memberDesc = (CallableMemberDescriptor) maybeEmtpyMemberDesc;
+ Object[] pojoArgsWithArray;
+ Object argsOrErrorIdx = replaceVarargsSectionWithArray(pojoArgs, tmArgs, memberDesc, unwrapper);
+ if (argsOrErrorIdx instanceof Object[]) {
+ pojoArgsWithArray = (Object[]) argsOrErrorIdx;
+ } else {
+ return EmptyMemberAndArguments.noCompatibleOverload(((Integer) argsOrErrorIdx).intValue());
+ }
+ if (typesFlags != null) {
+ // Note that overloaded method selection has already accounted for overflow errors when the method
+ // was selected. So this forced conversion shouldn't cause such corruption. Except, conversion from
+ // BigDecimal is allowed to overflow for backward-compatibility.
+ forceNumberArgumentsToParameterTypes(pojoArgsWithArray, memberDesc.getParamTypes(), typesFlags);
+ }
+ return new MemberAndArguments(memberDesc, pojoArgsWithArray);
+ } else {
+ return EmptyMemberAndArguments.from((EmptyCallableMemberDescriptor) maybeEmtpyMemberDesc, pojoArgs);
+ }
+ }
+
+ /**
+ * Converts a flat argument list to one where the last argument is an array that collects the varargs, also
+ * re-unwraps the varargs to the component type. Note that this couldn't be done until we had the concrete
+ * member selected.
+ *
+ * @return An {@code Object[]} if everything went well, or an {@code Integer} the
+ * order (1-based index) of the argument that couldn't be unwrapped.
+ */
+ private Object replaceVarargsSectionWithArray(
+ Object[] args, List modelArgs, CallableMemberDescriptor memberDesc, DefaultObjectWrapper unwrapper)
+ throws TemplateModelException {
+ final Class[] paramTypes = memberDesc.getParamTypes();
+ final int paramCount = paramTypes.length;
+ final Class varArgsCompType = paramTypes[paramCount - 1].getComponentType();
+ final int totalArgCount = args.length;
+ final int fixArgCount = paramCount - 1;
+ if (args.length != paramCount) {
+ Object[] packedArgs = new Object[paramCount];
+ System.arraycopy(args, 0, packedArgs, 0, fixArgCount);
+ Object varargs = Array.newInstance(varArgsCompType, totalArgCount - fixArgCount);
+ for (int i = fixArgCount; i < totalArgCount; ++i) {
+ Object val = unwrapper.tryUnwrapTo((TemplateModel) modelArgs.get(i), varArgsCompType);
+ if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ return Integer.valueOf(i + 1);
+ }
+ Array.set(varargs, i - fixArgCount, val);
+ }
+ packedArgs[fixArgCount] = varargs;
+ return packedArgs;
+ } else {
+ Object val = unwrapper.tryUnwrapTo((TemplateModel) modelArgs.get(fixArgCount), varArgsCompType);
+ if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ return Integer.valueOf(fixArgCount + 1);
+ }
+ Object array = Array.newInstance(varArgsCompType, 1);
+ Array.set(array, 0, val);
+ args[fixArgCount] = array;
+ return args;
+ }
+ }
+
+}
\ 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/PrimtiveArrayBackedReadOnlyList.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java b/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java
new file mode 100644
index 0000000..46e1cdf
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java
@@ -0,0 +1,47 @@
+/*
+ * 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.lang.reflect.Array;
+import java.util.AbstractList;
+
+/**
+ * Similar to {@link NonPrimitiveArrayBackedReadOnlyList}, but uses reflection so that it works with primitive arrays
+ * too.
+ */
+class PrimtiveArrayBackedReadOnlyList extends AbstractList {
+
+ private final Object array;
+
+ PrimtiveArrayBackedReadOnlyList(Object array) {
+ this.array = array;
+ }
+
+ @Override
+ public Object get(int index) {
+ return Array.get(array, index);
+ }
+
+ @Override
+ public int size() {
+ return Array.getLength(array);
+ }
+
+}
\ 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/ReflectionCallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java b/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
new file mode 100644
index 0000000..61eb87c
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
@@ -0,0 +1,95 @@
+/*
+ * 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.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * The most commonly used {@link CallableMemberDescriptor} implementation.
+ */
+final class ReflectionCallableMemberDescriptor extends CallableMemberDescriptor {
+
+ private final Member/*Method|Constructor*/ member;
+
+ /**
+ * Don't modify this array!
+ */
+ final Class[] paramTypes;
+
+ ReflectionCallableMemberDescriptor(Method member, Class[] paramTypes) {
+ this.member = member;
+ this.paramTypes = paramTypes;
+ }
+
+ ReflectionCallableMemberDescriptor(Constructor member, Class[] paramTypes) {
+ this.member = member;
+ this.paramTypes = paramTypes;
+ }
+
+ @Override
+ TemplateModel invokeMethod(DefaultObjectWrapper ow, Object obj, Object[] args)
+ throws TemplateModelException, InvocationTargetException, IllegalAccessException {
+ return ow.invokeMethod(obj, (Method) member, args);
+ }
+
+ @Override
+ Object invokeConstructor(DefaultObjectWrapper ow, Object[] args)
+ throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
+ return ((Constructor) member).newInstance(args);
+ }
+
+ @Override
+ String getDeclaration() {
+ return _MethodUtil.toString(member);
+ }
+
+ @Override
+ boolean isConstructor() {
+ return member instanceof Constructor;
+ }
+
+ @Override
+ boolean isStatic() {
+ return (member.getModifiers() & Modifier.STATIC) != 0;
+ }
+
+ @Override
+ boolean isVarargs() {
+ return _MethodUtil.isVarargs(member);
+ }
+
+ @Override
+ Class[] getParamTypes() {
+ return paramTypes;
+ }
+
+ @Override
+ String getName() {
+ return member.getName();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java b/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
new file mode 100644
index 0000000..27bdf96
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
@@ -0,0 +1,190 @@
+/*
+ * 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.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import org.apache.freemarker.core._DelayedJQuote;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * <p>A hash model that wraps a resource bundle. Makes it convenient to store
+ * localized content in the data model. It also acts as a method model that will
+ * take a resource key and arbitrary number of arguments and will apply
+ * {@link MessageFormat} with arguments on the string represented by the key.</p>
+ *
+ * <p>Typical usages:</p>
+ * <ul>
+ * <li><tt>bundle.resourceKey</tt> will retrieve the object from resource bundle
+ * with key <tt>resourceKey</tt></li>
+ * <li><tt>bundle("patternKey", arg1, arg2, arg3)</tt> will retrieve the string
+ * from resource bundle with key <tt>patternKey</tt>, and will use it as a pattern
+ * for MessageFormat with arguments arg1, arg2 and arg3</li>
+ * </ul>
+ */
+public class ResourceBundleModel
+ extends
+ BeanModel
+ implements
+ TemplateMethodModelEx {
+ static final ModelFactory FACTORY =
+ new ModelFactory()
+ {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new ResourceBundleModel((ResourceBundle) object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+ private Hashtable formats = null;
+
+ public ResourceBundleModel(ResourceBundle bundle, DefaultObjectWrapper wrapper) {
+ super(bundle, wrapper);
+ }
+
+ /**
+ * Overridden to invoke the getObject method of the resource bundle.
+ */
+ @Override
+ protected TemplateModel invokeGenericGet(Map keyMap, Class clazz, String key)
+ throws TemplateModelException {
+ try {
+ return wrap(((ResourceBundle) object).getObject(key));
+ } catch (MissingResourceException e) {
+ throw new _TemplateModelException(e,
+ "No ", new _DelayedJQuote(key), " key in the ResourceBundle. "
+ + "Note that conforming to the ResourceBundle Java API, this is an error and not just "
+ + "a missing sub-variable (a null).");
+ }
+ }
+
+ /**
+ * Returns true if this bundle contains no objects.
+ */
+ @Override
+ public boolean isEmpty() {
+ return !((ResourceBundle) object).getKeys().hasMoreElements() &&
+ super.isEmpty();
+ }
+
+ @Override
+ public int size() {
+ return keySet().size();
+ }
+
+ @Override
+ protected Set keySet() {
+ Set set = super.keySet();
+ Enumeration e = ((ResourceBundle) object).getKeys();
+ while (e.hasMoreElements()) {
+ set.add(e.nextElement());
+ }
+ return set;
+ }
+
+ /**
+ * Takes first argument as a resource key, looks up a string in resource bundle
+ * with this key, then applies a MessageFormat.format on the string with the
+ * rest of the arguments. The created MessageFormats are cached for later reuse.
+ */
+ @Override
+ public Object exec(List arguments)
+ throws TemplateModelException {
+ // Must have at least one argument - the key
+ if (arguments.size() < 1)
+ throw new TemplateModelException("No message key was specified");
+ // Read it
+ Iterator it = arguments.iterator();
+ String key = unwrap((TemplateModel) it.next()).toString();
+ try {
+ if (!it.hasNext()) {
+ return wrap(((ResourceBundle) object).getObject(key));
+ }
+
+ // Copy remaining arguments into an Object[]
+ int args = arguments.size() - 1;
+ Object[] params = new Object[args];
+ for (int i = 0; i < args; ++i)
+ params[i] = unwrap((TemplateModel) it.next());
+
+ // Invoke format
+ return new StringModel(format(key, params), wrapper);
+ } catch (MissingResourceException e) {
+ throw new TemplateModelException("No such key: " + key);
+ } catch (Exception e) {
+ throw new TemplateModelException(e.getMessage());
+ }
+ }
+
+ /**
+ * Provides direct access to caching format engine from code (instead of from script).
+ */
+ public String format(String key, Object[] params)
+ throws MissingResourceException {
+ // Check to see if we already have a cache for message formats
+ // and construct it if we don't
+ // NOTE: this block statement should be synchronized. However
+ // concurrent creation of two caches will have no harmful
+ // consequences, and we avoid a performance hit.
+ /* synchronized(this) */
+ {
+ if (formats == null)
+ formats = new Hashtable();
+ }
+
+ MessageFormat format = null;
+ // Check to see if we already have a requested MessageFormat cached
+ // and construct it if we don't
+ // NOTE: this block statement should be synchronized. However
+ // concurrent creation of two formats will have no harmful
+ // consequences, and we avoid a performance hit.
+ /* synchronized(formats) */
+ {
+ format = (MessageFormat) formats.get(key);
+ if (format == null) {
+ format = new MessageFormat(((ResourceBundle) object).getString(key));
+ format.setLocale(getBundle().getLocale());
+ formats.put(key, format);
+ }
+ }
+
+ // Perform the formatting. We synchronize on it in case it
+ // contains date formatting, which is not thread-safe.
+ synchronized (format) {
+ return format.format(params);
+ }
+ }
+
+ public ResourceBundle getBundle() {
+ return (ResourceBundle) object;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
new file mode 100644
index 0000000..96da1b8
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
@@ -0,0 +1,68 @@
+/*
+ * 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.util.AbstractList;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+
+/**
+ */
+class SequenceAdapter extends AbstractList implements TemplateModelAdapter {
+ private final DefaultObjectWrapper wrapper;
+ private final TemplateSequenceModel model;
+
+ SequenceAdapter(TemplateSequenceModel model, DefaultObjectWrapper wrapper) {
+ this.model = model;
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public TemplateModel getTemplateModel() {
+ return model;
+ }
+
+ @Override
+ public int size() {
+ try {
+ return model.size();
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public Object get(int index) {
+ try {
+ return wrapper.unwrap(model.get(index));
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ public TemplateSequenceModel getTemplateSequenceModel() {
+ return model;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java
new file mode 100644
index 0000000..a589040
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java
@@ -0,0 +1,32 @@
+/*
+ * 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.util.Set;
+
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+
+/**
+ */
+class SetAdapter extends CollectionAdapter implements Set {
+ SetAdapter(TemplateCollectionModel model, DefaultObjectWrapper wrapper) {
+ super(model, wrapper);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
index 40c33f7..fa44144 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
@@ -37,7 +37,6 @@ import org.apache.freemarker.core.model.TemplateHashModelEx2;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelException;
import org.apache.freemarker.core.model.WrappingTemplateModel;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
/**
* A simple implementation of the {@link TemplateHashModelEx} interface, using its own underlying {@link Map} or
@@ -308,13 +307,13 @@ public class SimpleHash extends WrappingTemplateModel implements TemplateHashMod
}
// Create a copy to maintain immutability semantics and
// Do nested unwrapping of elements if necessary.
- BeansWrapper bw = _StaticObjectWrappers.BEANS_WRAPPER;
+ DefaultObjectWrapper ow = _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER;
for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
Object value = entry.getValue();
if (value instanceof TemplateModel) {
- value = bw.unwrap((TemplateModel) value);
+ value = ow.unwrap((TemplateModel) value);
}
m.put(key, value);
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/SimpleMapModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleMapModel.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleMapModel.java
new file mode 100644
index 0000000..cf9836e
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleMapModel.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.util.List;
+import java.util.Map;
+
+import org.apache.freemarker.core.model.AdapterTemplateModel;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.RichObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
+import org.apache.freemarker.core.model.WrapperTemplateModel;
+import org.apache.freemarker.core.model.WrappingTemplateModel;
+
+/**
+ * Model used by {@link DefaultObjectWrapper} when <tt>simpleMapWrapper</tt>
+ * mode is enabled. Provides a simple hash model interface to the
+ * underlying map (does not copy like {@link org.apache.freemarker.core.model.impl.SimpleHash}),
+ * and a method interface to non-string keys.
+ */
+public class SimpleMapModel extends WrappingTemplateModel
+implements TemplateHashModelEx2, TemplateMethodModelEx, AdapterTemplateModel,
+WrapperTemplateModel, TemplateModelWithAPISupport {
+ static final ModelFactory FACTORY =
+ new ModelFactory()
+ {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new SimpleMapModel((Map) object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+ private final Map map;
+
+ public SimpleMapModel(Map map, DefaultObjectWrapper wrapper) {
+ super(wrapper);
+ this.map = map;
+ }
+
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ Object val = map.get(key);
+ if (val == null) {
+ if (key.length() == 1) {
+ // just check for Character key if this is a single-character string
+ Character charKey = Character.valueOf(key.charAt(0));
+ val = map.get(charKey);
+ if (val == null && !(map.containsKey(key) || map.containsKey(charKey))) {
+ return null;
+ }
+ } else if (!map.containsKey(key)) {
+ return null;
+ }
+ }
+ return wrap(val);
+ }
+
+ @Override
+ public Object exec(List args) throws TemplateModelException {
+ Object key = ((DefaultObjectWrapper) getObjectWrapper()).unwrap((TemplateModel) args.get(0));
+ Object value = map.get(key);
+ if (value == null && !map.containsKey(key)) {
+ return null;
+ }
+ return wrap(value);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public TemplateCollectionModel keys() {
+ return new CollectionAndSequence(new SimpleSequence(map.keySet(), getObjectWrapper()));
+ }
+
+ @Override
+ public TemplateCollectionModel values() {
+ return new CollectionAndSequence(new SimpleSequence(map.values(), getObjectWrapper()));
+ }
+
+ @Override
+ public KeyValuePairIterator keyValuePairIterator() {
+ return new MapKeyValuePairIterator(map, getObjectWrapper());
+ }
+
+ @Override
+ public Object getAdaptedObject(Class hint) {
+ return map;
+ }
+
+ @Override
+ public Object getWrappedObject() {
+ return map;
+ }
+
+ @Override
+ public TemplateModel getAPI() throws TemplateModelException {
+ return ((RichObjectWrapper) getObjectWrapper()).wrapAsAPI(map);
+ }
+}
\ 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/SimpleMethod.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
new file mode 100644
index 0000000..4fc81d0
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
@@ -0,0 +1,174 @@
+/*
+ * 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.lang.reflect.Array;
+import java.lang.reflect.Member;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.freemarker.core._DelayedFTLTypeDescription;
+import org.apache.freemarker.core._DelayedOrdinal;
+import org.apache.freemarker.core._ErrorDescriptionBuilder;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util._ClassUtil;
+
+/**
+ * This class is used for as a base for non-overloaded method models and for constructors.
+ * (For overloaded methods and constructors see {@link OverloadedMethods}.)
+ */
+class SimpleMethod {
+
+ static final String MARKUP_OUTPUT_TO_STRING_TIP
+ = "A markup output value can be converted to markup string like value?markup_string. "
+ + "But consider if the Java method whose argument it will be can handle markup strings properly.";
+
+ private final Member member;
+ private final Class[] argTypes;
+
+ protected SimpleMethod(Member member, Class[] argTypes) {
+ this.member = member;
+ this.argTypes = argTypes;
+ }
+
+ Object[] unwrapArguments(List arguments, DefaultObjectWrapper wrapper) throws TemplateModelException {
+ if (arguments == null) {
+ arguments = Collections.EMPTY_LIST;
+ }
+ boolean isVarArg = _MethodUtil.isVarargs(member);
+ int typesLen = argTypes.length;
+ if (isVarArg) {
+ if (typesLen - 1 > arguments.size()) {
+ throw new _TemplateModelException(
+ _MethodUtil.invocationErrorMessageStart(member),
+ " takes at least ", Integer.valueOf(typesLen - 1),
+ typesLen - 1 == 1 ? " argument" : " arguments", ", but ",
+ Integer.valueOf(arguments.size()), " was given.");
+ }
+ } else if (typesLen != arguments.size()) {
+ throw new _TemplateModelException(
+ _MethodUtil.invocationErrorMessageStart(member),
+ " takes ", Integer.valueOf(typesLen), typesLen == 1 ? " argument" : " arguments", ", but ",
+ Integer.valueOf(arguments.size()), " was given.");
+ }
+
+ return unwrapArguments(arguments, argTypes, isVarArg, wrapper);
+ }
+
+ private Object[] unwrapArguments(List args, Class[] argTypes, boolean isVarargs,
+ DefaultObjectWrapper w)
+ throws TemplateModelException {
+ if (args == null) return null;
+
+ int typesLen = argTypes.length;
+ int argsLen = args.size();
+
+ Object[] unwrappedArgs = new Object[typesLen];
+
+ // Unwrap arguments:
+ Iterator it = args.iterator();
+ int normalArgCnt = isVarargs ? typesLen - 1 : typesLen;
+ int argIdx = 0;
+ while (argIdx < normalArgCnt) {
+ Class argType = argTypes[argIdx];
+ TemplateModel argVal = (TemplateModel) it.next();
+ Object unwrappedArgVal = w.tryUnwrapTo(argVal, argType);
+ if (unwrappedArgVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ throw createArgumentTypeMismarchException(argIdx, argVal, argType);
+ }
+ if (unwrappedArgVal == null && argType.isPrimitive()) {
+ throw createNullToPrimitiveArgumentException(argIdx, argType);
+ }
+
+ unwrappedArgs[argIdx++] = unwrappedArgVal;
+ }
+ if (isVarargs) {
+ // The last argType, which is the vararg type, wasn't processed yet.
+
+ Class varargType = argTypes[typesLen - 1];
+ Class varargItemType = varargType.getComponentType();
+ if (!it.hasNext()) {
+ unwrappedArgs[argIdx++] = Array.newInstance(varargItemType, 0);
+ } else {
+ TemplateModel argVal = (TemplateModel) it.next();
+
+ Object unwrappedArgVal;
+ // We first try to treat the last argument as a vararg *array*.
+ // This is consistent to what OverloadedVarArgMethod does.
+ if (argsLen - argIdx == 1
+ && (unwrappedArgVal = w.tryUnwrapTo(argVal, varargType))
+ != ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ // It was a vararg array.
+ unwrappedArgs[argIdx++] = unwrappedArgVal;
+ } else {
+ // It wasn't a vararg array, so we assume it's a vararg
+ // array *item*, possibly followed by further ones.
+ int varargArrayLen = argsLen - argIdx;
+ Object varargArray = Array.newInstance(varargItemType, varargArrayLen);
+ for (int varargIdx = 0; varargIdx < varargArrayLen; varargIdx++) {
+ TemplateModel varargVal = (TemplateModel) (varargIdx == 0 ? argVal : it.next());
+ Object unwrappedVarargVal = w.tryUnwrapTo(varargVal, varargItemType);
+ if (unwrappedVarargVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ throw createArgumentTypeMismarchException(
+ argIdx + varargIdx, varargVal, varargItemType);
+ }
+
+ if (unwrappedVarargVal == null && varargItemType.isPrimitive()) {
+ throw createNullToPrimitiveArgumentException(argIdx + varargIdx, varargItemType);
+ }
+ Array.set(varargArray, varargIdx, unwrappedVarargVal);
+ }
+ unwrappedArgs[argIdx++] = varargArray;
+ }
+ }
+ }
+
+ return unwrappedArgs;
+ }
+
+ private TemplateModelException createArgumentTypeMismarchException(
+ int argIdx, TemplateModel argVal, Class targetType) {
+ _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
+ _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: Can't convert the ",
+ new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
+ " argument's value to the target Java type, ", _ClassUtil.getShortClassName(targetType),
+ ". The type of the actual value was: ", new _DelayedFTLTypeDescription(argVal));
+ if (argVal instanceof TemplateMarkupOutputModel && (targetType.isAssignableFrom(String.class))) {
+ desc.tip(MARKUP_OUTPUT_TO_STRING_TIP);
+ }
+ return new _TemplateModelException(desc);
+ }
+
+ private TemplateModelException createNullToPrimitiveArgumentException(int argIdx, Class targetType) {
+ return new _TemplateModelException(
+ _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: The value of the ",
+ new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
+ " argument was null, but the target Java parameter type (", _ClassUtil.getShortClassName(targetType),
+ ") is primitive and so can't store null.");
+ }
+
+ protected Member getMember() {
+ return member;
+ }
+}
\ 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/SimpleMethodModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethodModel.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethodModel.java
new file mode 100644
index 0000000..943d8f0
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethodModel.java
@@ -0,0 +1,133 @@
+/*
+ * 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.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+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.util.FTLUtil;
+
+/**
+ * A class that will wrap a reflected method call into a
+ * {@link org.apache.freemarker.core.model.TemplateMethodModel} interface.
+ * It is used by {@link BeanModel} to wrap reflected method calls
+ * for non-overloaded methods.
+ */
+public final class SimpleMethodModel extends SimpleMethod
+ implements
+ TemplateMethodModelEx,
+ TemplateSequenceModel,
+ _UnexpectedTypeErrorExplainerTemplateModel {
+ private final Object object;
+ private final DefaultObjectWrapper wrapper;
+
+ /**
+ * Creates a model for a specific method on a specific object.
+ * @param object the object to call the method on, or {@code null} for a static method.
+ * @param method the method that will be invoked.
+ * @param argTypes Either pass in {@code Method#getParameterTypes() method.getParameterTypes()} here,
+ * or reuse an earlier result of that call (for speed). Not {@code null}.
+ */
+ SimpleMethodModel(Object object, Method method, Class[] argTypes,
+ DefaultObjectWrapper wrapper) {
+ super(method, argTypes);
+ this.object = object;
+ this.wrapper = wrapper;
+ }
+
+ /**
+ * Invokes the method, passing it the arguments from the list.
+ */
+ @Override
+ public Object exec(List arguments)
+ throws TemplateModelException {
+ try {
+ return wrapper.invokeMethod(object, (Method) getMember(),
+ unwrapArguments(arguments, wrapper));
+ } catch (TemplateModelException e) {
+ throw e;
+ } catch (Exception e) {
+ throw _MethodUtil.newInvocationTemplateModelException(object, getMember(), e);
+ }
+ }
+
+ @Override
+ public TemplateModel get(int index) throws TemplateModelException {
+ return (TemplateModel) exec(Collections.singletonList(
+ new SimpleNumber(Integer.valueOf(index))));
+ }
+
+ @Override
+ public int size() throws TemplateModelException {
+ throw new TemplateModelException(
+ "Getting the number of items or enumerating the items is not supported on this "
+ + FTLUtil.getTypeDescription(this) + " value.\n"
+ + "("
+ + "Hint 1: Maybe you wanted to call this method first and then do something with its return value. "
+ + "Hint 2: Getting items by intex possibly works, hence it's a \"+sequence\"."
+ + ")");
+ }
+
+ @Override
+ public String toString() {
+ return getMember().toString();
+ }
+
+ /**
+ * Implementation of experimental interface; don't use it, no backward compatibility guarantee!
+ */
+ @Override
+ public Object[] explainTypeError(Class[] expectedClasses) {
+ final Member member = getMember();
+ if (!(member instanceof Method)) {
+ return null; // This shouldn't occur
+ }
+ Method m = (Method) member;
+
+ final Class returnType = m.getReturnType();
+ if (returnType == null || returnType == void.class || returnType == Void.class) {
+ return null; // Calling it won't help
+ }
+
+ String mName = m.getName();
+ if (mName.startsWith("get") && mName.length() > 3 && Character.isUpperCase(mName.charAt(3))
+ && (m.getParameterTypes().length == 0)) {
+ return new Object[] {
+ "Maybe using obj.something instead of obj.getSomething will yield the desired value." };
+ } else if (mName.startsWith("is") && mName.length() > 2 && Character.isUpperCase(mName.charAt(2))
+ && (m.getParameterTypes().length == 0)) {
+ return new Object[] {
+ "Maybe using obj.something instead of obj.isSomething will yield the desired value." };
+ } else {
+ return new Object[] {
+ "Maybe using obj.something(",
+ (m.getParameterTypes().length != 0 ? "params" : ""),
+ ") instead of obj.something will yield the desired value" };
+ }
+ }
+
+}
\ 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/SimpleObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleObjectWrapper.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleObjectWrapper.java
index 61378f0..281b8ac 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/SimpleObjectWrapper.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleObjectWrapper.java
@@ -23,7 +23,7 @@ import org.apache.freemarker.core.Version;
import org.apache.freemarker.core.model.TemplateHashModel;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
+import org.w3c.dom.Node;
/**
* A restricted object wrapper that will not expose arbitrary object, just those that directly correspond to the
@@ -33,21 +33,30 @@ import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
public class SimpleObjectWrapper extends DefaultObjectWrapper {
/**
- * @param incompatibleImprovements see in {@link BeansWrapper#BeansWrapper(Version)}.
+ * @param incompatibleImprovements see in {@link DefaultObjectWrapper#DefaultObjectWrapper(Version)}.
*
* @since 2.3.21
*/
public SimpleObjectWrapper(Version incompatibleImprovements) {
super(incompatibleImprovements);
}
-
+
+ @Override
+ protected TemplateModel handW3CNode(Node node) throws TemplateModelException {
+ throw newUnhandledTypeException(node);
+ }
+
/**
* Called if a type other than the simple ones we know about is passed in.
* In this implementation, this just throws an exception.
*/
@Override
protected TemplateModel handleUnknownType(Object obj) throws TemplateModelException {
- throw new TemplateModelException("SimpleObjectWrapper deliberately won't wrap this type: "
+ throw newUnhandledTypeException(obj);
+ }
+
+ private TemplateModelException newUnhandledTypeException(Object obj) throws TemplateModelException {
+ return new TemplateModelException("SimpleObjectWrapper deliberately won't wrap this type: "
+ obj.getClass().getName());
}
@@ -55,5 +64,5 @@ public class SimpleObjectWrapper extends DefaultObjectWrapper {
public TemplateHashModel wrapAsAPI(Object obj) throws TemplateModelException {
throw new TemplateModelException("SimpleObjectWrapper deliberately doesn't allow ?api.");
}
-
+
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java b/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
index b0d5b81..fa59010 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
@@ -33,7 +33,6 @@ import org.apache.freemarker.core.model.TemplateModelException;
import org.apache.freemarker.core.model.TemplateModelIterator;
import org.apache.freemarker.core.model.TemplateSequenceModel;
import org.apache.freemarker.core.model.WrappingTemplateModel;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
/**
* A simple implementation of the {@link TemplateSequenceModel} interface, using its own underlying {@link List} for
@@ -223,11 +222,11 @@ public class SimpleSequence extends WrappingTemplateModel implements TemplateSeq
throw new TemplateModelException("Error instantiating an object of type " + listClass.getName(),
e);
}
- BeansWrapper bw = _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER;
+ DefaultObjectWrapper ow = _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER;
for (int i = 0; i < list.size(); i++) {
Object elem = list.get(i);
if (elem instanceof TemplateModel) {
- elem = bw.unwrap((TemplateModel) elem);
+ elem = ow.unwrap((TemplateModel) elem);
}
result.add(elem);
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java b/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java
new file mode 100644
index 0000000..70f50f6
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java
@@ -0,0 +1,51 @@
+/*
+ * 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.model.ObjectWrapper;
+
+/**
+ * Marker interface useful when used together with {@link MethodAppearanceFineTuner} and such customizer objects, to
+ * indicate that it <b>doesn't contain reference to the {@link ObjectWrapper}</b> (so beware with non-static inner
+ * classes) and can be and should be used in call introspection cache keys. This also implies that you won't
+ * create many instances of the class, rather just reuse the same (or same few) instances over and over. Furthermore,
+ * the instances must be thread-safe. The typical pattern in which this instance should be used is like this:
+ *
+ * <pre>static class MyMethodAppearanceFineTuner implements MethodAppearanceFineTuner, SingletonCustomizer {
+ *
+ * // This is the singleton:
+ * static final MyMethodAppearanceFineTuner INSTANCE = new MyMethodAppearanceFineTuner();
+ *
+ * // Private, so it can't be constructed from outside this class.
+ * private MyMethodAppearanceFineTuner() { }
+ *
+ * @Override
+ * public void fineTuneMethodAppearance(...) {
+ * // Do something here, only using the parameters and maybe some other singletons.
+ * ...
+ * }
+ *
+ * }</pre>
+ *
+ * @since 2.3.21
+ */
+public interface SingletonCustomizer {
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java b/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
new file mode 100644
index 0000000..1a6263c
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
@@ -0,0 +1,177 @@
+/*
+ * 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.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.freemarker.core._CoreLogs;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.slf4j.Logger;
+
+/**
+ * Wraps the static fields and methods of a class in a
+ * {@link org.apache.freemarker.core.model.TemplateHashModel}.
+ * Fields are wrapped using {@link DefaultObjectWrapper#wrap(Object)}, and
+ * methods are wrapped into an appropriate {@link org.apache.freemarker.core.model.TemplateMethodModelEx} instance.
+ * Unfortunately, there is currently no support for bean property-style
+ * calls of static methods, similar to that in {@link BeanModel}.
+ */
+final class StaticModel implements TemplateHashModelEx {
+
+ private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER;
+
+ private final Class clazz;
+ private final DefaultObjectWrapper wrapper;
+ private final Map map = new HashMap();
+
+ StaticModel(Class clazz, DefaultObjectWrapper wrapper) throws TemplateModelException {
+ this.clazz = clazz;
+ this.wrapper = wrapper;
+ populate();
+ }
+
+ /**
+ * Returns the field or method named by the <tt>key</tt>
+ * parameter.
+ */
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ Object model = map.get(key);
+ // Simple method, overloaded method or final field -- these have cached
+ // template models
+ if (model instanceof TemplateModel)
+ return (TemplateModel) model;
+ // Non-final field; this must be evaluated on each call.
+ if (model instanceof Field) {
+ try {
+ return wrapper.getOuterIdentity().wrap(((Field) model).get(null));
+ } catch (IllegalAccessException e) {
+ throw new TemplateModelException(
+ "Illegal access for field " + key + " of class " + clazz.getName());
+ }
+ }
+
+ throw new TemplateModelException(
+ "No such key: " + key + " in class " + clazz.getName());
+ }
+
+ /**
+ * Returns true if there is at least one public static
+ * field or method in the underlying class.
+ */
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public TemplateCollectionModel keys() throws TemplateModelException {
+ return (TemplateCollectionModel) wrapper.getOuterIdentity().wrap(map.keySet());
+ }
+
+ @Override
+ public TemplateCollectionModel values() throws TemplateModelException {
+ return (TemplateCollectionModel) wrapper.getOuterIdentity().wrap(map.values());
+ }
+
+ private void populate() throws TemplateModelException {
+ if (!Modifier.isPublic(clazz.getModifiers())) {
+ throw new TemplateModelException(
+ "Can't wrap the non-public class " + clazz.getName());
+ }
+
+ if (wrapper.getExposureLevel() == DefaultObjectWrapper.EXPOSE_NOTHING) {
+ return;
+ }
+
+ Field[] fields = clazz.getFields();
+ for (Field field : fields) {
+ int mod = field.getModifiers();
+ if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
+ if (Modifier.isFinal(mod))
+ try {
+ // public static final fields are evaluated once and
+ // stored in the map
+ map.put(field.getName(), wrapper.getOuterIdentity().wrap(field.get(null)));
+ } catch (IllegalAccessException e) {
+ // Intentionally ignored
+ }
+ else
+ // This is a special flagging value: Field in the map means
+ // that this is a non-final field, and it must be evaluated
+ // on each get() call.
+ map.put(field.getName(), field);
+ }
+ }
+ if (wrapper.getExposureLevel() < DefaultObjectWrapper.EXPOSE_PROPERTIES_ONLY) {
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ int mod = method.getModifiers();
+ if (Modifier.isPublic(mod) && Modifier.isStatic(mod)
+ && wrapper.getClassIntrospector().isAllowedToExpose(method)) {
+ String name = method.getName();
+ Object obj = map.get(name);
+ if (obj instanceof Method) {
+ OverloadedMethods overloadedMethods = new OverloadedMethods();
+ overloadedMethods.addMethod((Method) obj);
+ overloadedMethods.addMethod(method);
+ map.put(name, overloadedMethods);
+ } else if (obj instanceof OverloadedMethods) {
+ OverloadedMethods overloadedMethods = (OverloadedMethods) obj;
+ overloadedMethods.addMethod(method);
+ } else {
+ if (obj != null) {
+ if (LOG.isInfoEnabled()) {
+ LOG.info("Overwriting value [" + obj + "] for " +
+ " key '" + name + "' with [" + method +
+ "] in static model for " + clazz.getName());
+ }
+ }
+ map.put(name, method);
+ }
+ }
+ }
+ for (Iterator entries = map.entrySet().iterator(); entries.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) entries.next();
+ Object value = entry.getValue();
+ if (value instanceof Method) {
+ Method method = (Method) value;
+ entry.setValue(new SimpleMethodModel(null, method,
+ method.getParameterTypes(), wrapper));
+ } else if (value instanceof OverloadedMethods) {
+ entry.setValue(new OverloadedMethodsModel(null, (OverloadedMethods) value, wrapper));
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java b/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java
new file mode 100644
index 0000000..04dd3a5
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java
@@ -0,0 +1,43 @@
+/*
+ * 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.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * Utility class for instantiating {@link StaticModel} instances from
+ * templates. If your template's data model contains an instance of
+ * StaticModels (named, say <tt>StaticModels</tt>), then you can
+ * instantiate an arbitrary StaticModel using get syntax (i.e.
+ * <tt>StaticModels["java.lang.System"].currentTimeMillis()</tt>).
+ */
+class StaticModels extends ClassBasedModelFactory {
+
+ StaticModels(DefaultObjectWrapper wrapper) {
+ super(wrapper);
+ }
+
+ @Override
+ protected TemplateModel createModel(Class clazz)
+ throws TemplateModelException {
+ return new StaticModel(clazz, getWrapper());
+ }
+}
\ 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/StringModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/StringModel.java b/src/main/java/org/apache/freemarker/core/model/impl/StringModel.java
new file mode 100644
index 0000000..499c291
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/StringModel.java
@@ -0,0 +1,63 @@
+/*
+ * 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.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+
+/**
+ * Subclass of {@link BeanModel} that exposes the return value of the {@link
+ * java.lang.Object#toString()} method through the {@link TemplateScalarModel}
+ * interface.
+ */
+public class StringModel extends BeanModel
+implements TemplateScalarModel {
+ static final ModelFactory FACTORY =
+ new ModelFactory()
+ {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new StringModel(object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+ /**
+ * Creates a new model that wraps the specified object with BeanModel + scalar
+ * functionality.
+ * @param object the object to wrap into a model.
+ * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+ * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+ * model gains many attributes from its wrapper, including the caching
+ * behavior, method exposure level, method-over-item shadowing policy etc.
+ */
+ public StringModel(Object object, DefaultObjectWrapper wrapper) {
+ super(object, wrapper);
+ }
+
+ /**
+ * Returns the result of calling {@link Object#toString()} on the wrapped
+ * object.
+ */
+ @Override
+ public String getAsString() {
+ return object.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java b/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
new file mode 100644
index 0000000..d81ab74
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
@@ -0,0 +1,130 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Flag values and masks for "type flags". "Type flags" is a set of bits that store information about the possible
+ * destination types at a parameter position of overloaded methods.
+ */
+class TypeFlags {
+
+ /**
+ * Indicates that the unwrapping hint will not be a specific numerical type; it must not be set if there's no
+ * numerical type at the given parameter index.
+ */
+ static final int WIDENED_NUMERICAL_UNWRAPPING_HINT = 1;
+
+ static final int BYTE = 4;
+ static final int SHORT = 8;
+ static final int INTEGER = 16;
+ static final int LONG = 32;
+ static final int FLOAT = 64;
+ static final int DOUBLE = 128;
+ static final int BIG_INTEGER = 256;
+ static final int BIG_DECIMAL = 512;
+ static final int UNKNOWN_NUMERICAL_TYPE = 1024;
+
+ static final int ACCEPTS_NUMBER = 0x800;
+ static final int ACCEPTS_DATE = 0x1000;
+ static final int ACCEPTS_STRING = 0x2000;
+ static final int ACCEPTS_BOOLEAN = 0x4000;
+ static final int ACCEPTS_MAP = 0x8000;
+ static final int ACCEPTS_LIST = 0x10000;
+ static final int ACCEPTS_SET = 0x20000;
+ static final int ACCEPTS_ARRAY = 0x40000;
+
+ /**
+ * Indicates the presence of the char or Character type
+ */
+ static final int CHARACTER = 0x80000;
+
+ static final int ACCEPTS_ANY_OBJECT = ACCEPTS_NUMBER | ACCEPTS_DATE | ACCEPTS_STRING | ACCEPTS_BOOLEAN
+ | ACCEPTS_MAP | ACCEPTS_LIST | ACCEPTS_SET | ACCEPTS_ARRAY;
+
+ static final int MASK_KNOWN_INTEGERS = BYTE | SHORT | INTEGER | LONG | BIG_INTEGER;
+ static final int MASK_KNOWN_NONINTEGERS = FLOAT | DOUBLE | BIG_DECIMAL;
+ static final int MASK_ALL_KNOWN_NUMERICALS = MASK_KNOWN_INTEGERS | MASK_KNOWN_NONINTEGERS;
+ static final int MASK_ALL_NUMERICALS = MASK_ALL_KNOWN_NUMERICALS | UNKNOWN_NUMERICAL_TYPE;
+
+ static int classToTypeFlags(Class pClass) {
+ // We start with the most frequent cases
+ if (pClass == Object.class) {
+ return ACCEPTS_ANY_OBJECT;
+ } else if (pClass == String.class) {
+ return ACCEPTS_STRING;
+ } else if (pClass.isPrimitive()) {
+ if (pClass == Integer.TYPE) return INTEGER | ACCEPTS_NUMBER;
+ else if (pClass == Long.TYPE) return LONG | ACCEPTS_NUMBER;
+ else if (pClass == Double.TYPE) return DOUBLE | ACCEPTS_NUMBER;
+ else if (pClass == Float.TYPE) return FLOAT | ACCEPTS_NUMBER;
+ else if (pClass == Byte.TYPE) return BYTE | ACCEPTS_NUMBER;
+ else if (pClass == Short.TYPE) return SHORT | ACCEPTS_NUMBER;
+ else if (pClass == Character.TYPE) return CHARACTER;
+ else if (pClass == Boolean.TYPE) return ACCEPTS_BOOLEAN;
+ else return 0;
+ } else if (Number.class.isAssignableFrom(pClass)) {
+ if (pClass == Integer.class) return INTEGER | ACCEPTS_NUMBER;
+ else if (pClass == Long.class) return LONG | ACCEPTS_NUMBER;
+ else if (pClass == Double.class) return DOUBLE | ACCEPTS_NUMBER;
+ else if (pClass == Float.class) return FLOAT | ACCEPTS_NUMBER;
+ else if (pClass == Byte.class) return BYTE | ACCEPTS_NUMBER;
+ else if (pClass == Short.class) return SHORT | ACCEPTS_NUMBER;
+ else if (BigDecimal.class.isAssignableFrom(pClass)) return BIG_DECIMAL | ACCEPTS_NUMBER;
+ else if (BigInteger.class.isAssignableFrom(pClass)) return BIG_INTEGER | ACCEPTS_NUMBER;
+ else return UNKNOWN_NUMERICAL_TYPE | ACCEPTS_NUMBER;
+ } else if (pClass.isArray()) {
+ return ACCEPTS_ARRAY;
+ } else {
+ int flags = 0;
+ if (pClass.isAssignableFrom(String.class)) {
+ flags |= ACCEPTS_STRING;
+ }
+ if (pClass.isAssignableFrom(Date.class)) {
+ flags |= ACCEPTS_DATE;
+ }
+ if (pClass.isAssignableFrom(Boolean.class)) {
+ flags |= ACCEPTS_BOOLEAN;
+ }
+ if (pClass.isAssignableFrom(Map.class)) {
+ flags |= ACCEPTS_MAP;
+ }
+ if (pClass.isAssignableFrom(List.class)) {
+ flags |= ACCEPTS_LIST;
+ }
+ if (pClass.isAssignableFrom(Set.class)) {
+ flags |= ACCEPTS_SET;
+ }
+
+ if (pClass == Character.class) {
+ flags |= CHARACTER;
+ }
+
+ return flags;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java
new file mode 100644
index 0000000..b54ddbf
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java
@@ -0,0 +1,112 @@
+/*
+ * 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.InputStream;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.freemarker.core._CoreLogs;
+import org.apache.freemarker.core.util._ClassUtil;
+import org.slf4j.Logger;
+
+class UnsafeMethods {
+
+ private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER;
+ private static final String UNSAFE_METHODS_PROPERTIES = "unsafeMethods.properties";
+ private static final Set UNSAFE_METHODS = createUnsafeMethodsSet();
+
+ private UnsafeMethods() { }
+
+ static boolean isUnsafeMethod(Method method) {
+ return UNSAFE_METHODS.contains(method);
+ }
+
+ private static Set createUnsafeMethodsSet() {
+ Properties props = new Properties();
+ InputStream in = DefaultObjectWrapper.class.getResourceAsStream(UNSAFE_METHODS_PROPERTIES);
+ if (in == null) {
+ throw new IllegalStateException("Class loader resource not found: "
+ + DefaultObjectWrapper.class.getPackage().getName() + UNSAFE_METHODS_PROPERTIES);
+ }
+ String methodSpec = null;
+ try {
+ try {
+ props.load(in);
+ } finally {
+ in.close();
+ }
+ Set set = new HashSet(props.size() * 4 / 3, 1f);
+ Map primClasses = createPrimitiveClassesMap();
+ for (Iterator iterator = props.keySet().iterator(); iterator.hasNext(); ) {
+ methodSpec = (String) iterator.next();
+ try {
+ set.add(parseMethodSpec(methodSpec, primClasses));
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ LOG.debug("Failed to get unsafe method", e);
+ }
+ }
+ return set;
+ } catch (Exception e) {
+ throw new RuntimeException("Could not load unsafe method " + methodSpec + " " + e.getClass().getName() + " " + e.getMessage());
+ }
+ }
+
+ private static Method parseMethodSpec(String methodSpec, Map primClasses)
+ throws ClassNotFoundException,
+ NoSuchMethodException {
+ int brace = methodSpec.indexOf('(');
+ int dot = methodSpec.lastIndexOf('.', brace);
+ Class clazz = _ClassUtil.forName(methodSpec.substring(0, dot));
+ String methodName = methodSpec.substring(dot + 1, brace);
+ String argSpec = methodSpec.substring(brace + 1, methodSpec.length() - 1);
+ StringTokenizer tok = new StringTokenizer(argSpec, ",");
+ int argcount = tok.countTokens();
+ Class[] argTypes = new Class[argcount];
+ for (int i = 0; i < argcount; i++) {
+ String argClassName = tok.nextToken();
+ argTypes[i] = (Class) primClasses.get(argClassName);
+ if (argTypes[i] == null) {
+ argTypes[i] = _ClassUtil.forName(argClassName);
+ }
+ }
+ return clazz.getMethod(methodName, argTypes);
+ }
+
+ private static Map createPrimitiveClassesMap() {
+ Map map = new HashMap();
+ map.put("boolean", Boolean.TYPE);
+ map.put("byte", Byte.TYPE);
+ map.put("char", Character.TYPE);
+ map.put("short", Short.TYPE);
+ map.put("int", Integer.TYPE);
+ map.put("long", Long.TYPE);
+ map.put("float", Float.TYPE);
+ map.put("double", Double.TYPE);
+ return map;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/_EnumModels.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_EnumModels.java b/src/main/java/org/apache/freemarker/core/model/impl/_EnumModels.java
new file mode 100644
index 0000000..19efe76
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/_EnumModels.java
@@ -0,0 +1,54 @@
+/*
+ * 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.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.freemarker.core.model.TemplateModel;
+
+/**
+ * Don't use this class; it's only public to work around Google App Engine Java
+ * compliance issues. FreeMarker developers only: treat this class as package-visible.
+ */
+public class _EnumModels extends ClassBasedModelFactory {
+
+ public _EnumModels(DefaultObjectWrapper wrapper) {
+ super(wrapper);
+ }
+
+ @Override
+ protected TemplateModel createModel(Class clazz) {
+ Object[] obj = clazz.getEnumConstants();
+ if (obj == null) {
+ // Return null - it'll manifest itself as undefined in the template.
+ // We're doing this rather than throw an exception as this way
+ // people can use someEnumModel?default({}) to gracefully fall back
+ // to an empty hash if they want to.
+ return null;
+ }
+ Map map = new LinkedHashMap();
+ for (Object anObj : obj) {
+ Enum value = (Enum) anObj;
+ map.put(value.name(), value);
+ }
+ return new SimpleMapModel(map, getWrapper());
+ }
+}
[04/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperMiscTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperMiscTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperMiscTest.java
deleted file mode 100644
index fd20bd6..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperMiscTest.java
+++ /dev/null
@@ -1,57 +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 static org.junit.Assert.*;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class BeansWrapperMiscTest {
-
- @Test
- public void booleans() throws Exception {
- final BeansWrapper bw = new BeansWrapper(Configuration.VERSION_3_0_0);
-
- assertTrue(((TemplateBooleanModel) bw.wrap(Boolean.TRUE)).getAsBoolean());
- assertFalse(((TemplateBooleanModel) bw.wrap(Boolean.FALSE)).getAsBoolean());
-
- TemplateHashModel tm = (TemplateHashModel) bw.wrap(Boolean.TRUE);
- assertNotNull(tm.get("hashCode"));
- assertNotNull(tm.get("class"));
- bw.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
- assertNull(tm.get("hashCode"));
- assertNotNull(tm.get("class"));
- bw.setExposureLevel(BeansWrapper.EXPOSE_NOTHING);
- assertNull(tm.get("hashCode"));
- assertNull(tm.get("class"));
- bw.setExposureLevel(BeansWrapper.EXPOSE_ALL);
- assertNotNull(tm.get("hashCode"));
- assertNotNull(tm.get("class"));
-
- assertSame(tm, bw.wrap(Boolean.TRUE));
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperReadOnlyTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperReadOnlyTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperReadOnlyTest.java
deleted file mode 100644
index 2ab748f..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperReadOnlyTest.java
+++ /dev/null
@@ -1,95 +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.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.model.ObjectWrapper;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-import org.apache.freemarker.core.util.WriteProtectable;
-import org.apache.freemarker.core.util._ClassUtil;
-
-import junit.framework.TestCase;
-
-/**
- * Tests if all JavaBean properties of the standard {@link ObjectWrapper} classes are locked by
- * {@link WriteProtectable#writeProtect()}.
- */
-public class BeansWrapperReadOnlyTest extends TestCase {
-
- private static final String EXPECTED_MESSAGE_PART = "write protected";
-
- public BeansWrapperReadOnlyTest(String name) {
- super(name);
- }
-
- public void testBeansWrapper() throws Exception {
- BeansWrapper bw = new BeansWrapper(Configuration.VERSION_3_0_0);
- bw.writeProtect();
- checkAllPropertiesReadOnly(bw);
- }
-
- public void testDefaultObjectWrapper() throws Exception {
- BeansWrapper bw = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
- bw.writeProtect();
- checkAllPropertiesReadOnly(bw);
- }
-
- 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);
- }
- }
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperSingletonsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperSingletonsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperSingletonsTest.java
deleted file mode 100644
index 027e6f2..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperSingletonsTest.java
+++ /dev/null
@@ -1,743 +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 static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.lang.ref.Reference;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.Version;
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.impl.SimpleObjectWrapper;
-import org.apache.freemarker.core.model.impl._ModelImplApi;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecision;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecisionInput;
-import org.apache.freemarker.test.util.TestUtil;
-
-import junit.framework.TestCase;
-
-public class BeansWrapperSingletonsTest extends TestCase {
-
- public BeansWrapperSingletonsTest(String name) {
- super(name);
- }
-
- @Override
- protected void setUp() throws Exception {
- BeansWrapperBuilder.clearInstanceCache(); // otherwise ClassInrospector cache couldn't become cleared in reality
- _ModelImplApi.DefaultObjectWrapperFactory_clearInstanceCache();
- BeansWrapperBuilder.clearInstanceCache();
- }
-
- public void testBeansWrapperFactoryEquals() throws Exception {
- assertEquals(Configuration.VERSION_3_0_0, new BeansWrapperBuilder(Configuration.VERSION_3_0_0).getIncompatibleImprovements());
- try {
- new BeansWrapperBuilder(TestUtil.getClosestFutureVersion());
- fail("Maybe you need to update this test for the new FreeMarker version");
- } catch (IllegalArgumentException e) {
- assertThat(e.getMessage(), containsString("upgrade"));
- }
-
- BeansWrapperBuilder pa1;
- BeansWrapperBuilder pa2;
-
- pa1 = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- pa2 = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- assertEquals(pa1, pa2);
-
- pa1.setSimpleMapWrapper(true);
- assertNotEquals(pa1, pa2);
- assertFalse(pa1.hashCode() == pa2.hashCode());
- pa2.setSimpleMapWrapper(true);
- assertEquals(pa1, pa2);
- assertTrue(pa1.hashCode() == pa2.hashCode());
-
- pa1.setExposeFields(true);
- assertNotEquals(pa1, pa2);
- assertFalse(pa1.hashCode() == pa2.hashCode());
- pa2.setExposeFields(true);
- assertEquals(pa1, pa2);
- assertTrue(pa1.hashCode() == pa2.hashCode());
-
- pa1.setExposureLevel(0);
- assertNotEquals(pa1, pa2);
- assertFalse(pa1.hashCode() == pa2.hashCode());
- pa2.setExposureLevel(0);
- assertEquals(pa1, pa2);
- assertTrue(pa1.hashCode() == pa2.hashCode());
-
- pa1.setExposureLevel(1);
- assertNotEquals(pa1, pa2);
- assertFalse(pa1.hashCode() == pa2.hashCode());
- pa2.setExposureLevel(1);
- assertEquals(pa1, pa2);
- assertTrue(pa1.hashCode() == pa2.hashCode());
-
- pa1.setDefaultDateType(TemplateDateModel.DATE);
- assertNotEquals(pa1, pa2);
- pa2.setDefaultDateType(TemplateDateModel.DATE);
- assertEquals(pa1, pa2);
- assertTrue(pa1.hashCode() == pa2.hashCode());
-
- pa1.setStrict(true);
- assertNotEquals(pa1, pa2);
- assertFalse(pa1.hashCode() == pa2.hashCode());
- pa2.setStrict(true);
- assertEquals(pa1, pa2);
- assertTrue(pa1.hashCode() == pa2.hashCode());
-
- pa1.setUseModelCache(true);
- assertNotEquals(pa1, pa2);
- assertFalse(pa1.hashCode() == pa2.hashCode());
- pa2.setUseModelCache(true);
- assertEquals(pa1, pa2);
- assertTrue(pa1.hashCode() == pa2.hashCode());
-
- AlphabeticalMethodSorter ms = new AlphabeticalMethodSorter(true);
- pa1.setMethodSorter(ms);
- assertNotEquals(pa1, pa2);
- pa2.setMethodSorter(ms);
- assertEquals(pa1, pa2);
- assertTrue(pa1.hashCode() == pa2.hashCode());
-
- MethodAppearanceFineTuner maft = new MethodAppearanceFineTuner() {
- @Override
- public void process(MethodAppearanceDecisionInput in, MethodAppearanceDecision out) { }
- };
- pa1.setMethodAppearanceFineTuner(maft);
- assertNotEquals(pa1, pa2);
- pa2.setMethodAppearanceFineTuner(maft);
- assertEquals(pa1, pa2);
- assertTrue(pa1.hashCode() == pa2.hashCode());
- }
-
- public void testBeansWrapperFactoryProducts() throws Exception {
- List<BeansWrapper> hardReferences = new LinkedList<>();
-
- assertEquals(0, getBeansWrapperInstanceCacheSize());
-
- {
- BeansWrapper bw = getBeansWrapperWithSetting(Configuration.VERSION_3_0_0, true);
- assertEquals(1, getBeansWrapperInstanceCacheSize());
- assertSame(bw.getClass(), BeansWrapper.class);
- assertEquals(Configuration.VERSION_3_0_0, bw.getIncompatibleImprovements());
- assertTrue(bw.isWriteProtected());
- assertTrue(bw.isSimpleMapWrapper());
- assertThat(bw.wrap(new HashMap()), instanceOf(SimpleMapModel.class));
- assertFalse(bw.isStrict());
- assertFalse(bw.getUseModelCache());
- assertEquals(TemplateDateModel.UNKNOWN, bw.getDefaultDateType());
- assertSame(bw, bw.getOuterIdentity());
- assertTrue(bw.isClassIntrospectionCacheRestricted());
- assertNull(bw.getMethodAppearanceFineTuner());
- assertNull(bw.getMethodSorter());
-
- try {
- bw.setExposeFields(true); // can't modify the settings of a (potential) singleton
- fail();
- } catch (IllegalStateException e) {
- assertThat(e.getMessage(), containsString("modify"));
- }
-
- assertSame(bw, getBeansWrapperWithSetting(Configuration.VERSION_3_0_0, true));
- assertEquals(1, getBeansWrapperInstanceCacheSize());
-
- hardReferences.add(bw);
- }
-
- {
- BeansWrapper bw = getBeansWrapperWithSetting(Configuration.VERSION_3_0_0, false);
- assertEquals(2, getBeansWrapperInstanceCacheSize());
- assertSame(bw.getClass(), BeansWrapper.class);
- assertEquals(Configuration.VERSION_3_0_0, bw.getIncompatibleImprovements());
- assertTrue(bw.isWriteProtected());
- assertFalse(bw.isSimpleMapWrapper());
- assertThat(bw.wrap(new HashMap()), instanceOf(MapModel.class));
-
- assertSame(bw, getBeansWrapperWithSetting(Configuration.VERSION_3_0_0, false));
-
- hardReferences.add(bw);
- }
-
- {
- BeansWrapperBuilder factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- factory.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
- BeansWrapper bw = factory.build();
- BeansWrapper bw2 = factory.build();
- assertEquals(3, getBeansWrapperInstanceCacheSize());
- assertSame(bw, bw2);
-
- assertSame(bw.getClass(), BeansWrapper.class);
- assertEquals(Configuration.VERSION_3_0_0, bw.getIncompatibleImprovements());
- assertTrue(bw.isWriteProtected());
- assertFalse(bw.isSimpleMapWrapper());
- assertThat(bw.wrap(new HashMap()), instanceOf(MapModel.class));
- assertEquals(BeansWrapper.EXPOSE_PROPERTIES_ONLY, bw.getExposureLevel());
- assertFalse(bw.isStrict());
- assertEquals(TemplateDateModel.UNKNOWN, bw.getDefaultDateType());
- assertSame(bw, bw.getOuterIdentity());
-
- hardReferences.add(bw);
- }
-
- {
- BeansWrapperBuilder factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- factory.setExposeFields(true);
- BeansWrapper bw = factory.build();
- BeansWrapper bw2 = factory.build();
- assertEquals(4, getBeansWrapperInstanceCacheSize());
- assertSame(bw, bw2);
-
- assertSame(bw.getClass(), BeansWrapper.class);
- assertEquals(Configuration.VERSION_3_0_0, bw.getIncompatibleImprovements());
- assertTrue(bw.isWriteProtected());
- assertFalse(bw.isSimpleMapWrapper());
- assertTrue(bw.isExposeFields());
-
- hardReferences.add(bw);
- }
-
- {
- BeansWrapperBuilder factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- factory.setStrict(true);
- factory.setDefaultDateType(TemplateDateModel.DATETIME);
- factory.setOuterIdentity(new SimpleObjectWrapper(Configuration.VERSION_3_0_0));
- BeansWrapper bw = factory.build();
- assertEquals(5, getBeansWrapperInstanceCacheSize());
- assertTrue(bw.isStrict());
- assertEquals(TemplateDateModel.DATETIME, bw.getDefaultDateType());
- assertSame(SimpleObjectWrapper.class, bw.getOuterIdentity().getClass());
-
- hardReferences.add(bw);
- }
-
- // Effect of reference and cache clearings:
- {
- BeansWrapper bw1 = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
- assertEquals(5, getBeansWrapperInstanceCacheSize());
- assertEquals(5, getBeansWrapperNonClearedInstanceCacheSize());
-
- clearBeansWrapperInstanceCacheReferences(false);
- assertEquals(5, getBeansWrapperInstanceCacheSize());
- assertEquals(0, getBeansWrapperNonClearedInstanceCacheSize());
-
- BeansWrapper bw2 = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
- assertNotSame(bw1, bw2);
- assertEquals(5, getBeansWrapperInstanceCacheSize());
- assertEquals(1, getBeansWrapperNonClearedInstanceCacheSize());
-
- assertSame(bw2, new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build());
- assertEquals(1, getBeansWrapperNonClearedInstanceCacheSize());
-
- clearBeansWrapperInstanceCacheReferences(true);
- BeansWrapper bw3 = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
- assertNotSame(bw2, bw3);
- assertEquals(1, getBeansWrapperInstanceCacheSize());
- assertEquals(1, getBeansWrapperNonClearedInstanceCacheSize());
- }
-
- {
- BeansWrapperBuilder factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- factory.setUseModelCache(true);
- BeansWrapper bw = factory.build();
- assertTrue(bw.getUseModelCache());
- assertEquals(2, getBeansWrapperInstanceCacheSize());
-
- hardReferences.add(bw);
- }
-
- assertTrue(hardReferences.size() != 0); // just to save it from GC until this line
- }
-
- private BeansWrapper getBeansWrapperWithSetting(Version ici, boolean simpleMapWrapper) {
- BeansWrapperBuilder f = new BeansWrapperBuilder(ici);
- f.setSimpleMapWrapper(simpleMapWrapper);
- return f.build();
- }
-
- public void testMultipleTCCLs() {
- List<BeansWrapper> hardReferences = new LinkedList<>();
-
- assertEquals(0, getBeansWrapperInstanceCacheSize());
-
- BeansWrapper bw1 = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
- assertEquals(1, getBeansWrapperInstanceCacheSize());
- hardReferences.add(bw1);
-
- ClassLoader oldTCCL = Thread.currentThread().getContextClassLoader();
- // Doesn't mater what, just be different from oldTCCL:
- ClassLoader newTCCL = oldTCCL == null ? getClass().getClassLoader() : null;
-
- BeansWrapper bw2;
- Thread.currentThread().setContextClassLoader(newTCCL);
- try {
- bw2 = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
- assertEquals(2, getBeansWrapperInstanceCacheSize());
- hardReferences.add(bw2);
-
- assertNotSame(bw1, bw2);
- assertSame(bw2, new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build());
- } finally {
- Thread.currentThread().setContextClassLoader(oldTCCL);
- }
-
- assertSame(bw1, new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build());
- assertEquals(2, getBeansWrapperInstanceCacheSize());
-
- BeansWrapper bw3;
- Thread.currentThread().setContextClassLoader(newTCCL);
- try {
- assertSame(bw2, new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build());
-
- BeansWrapperBuilder bwb = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- bwb.setExposeFields(true);
- bw3 = bwb.build();
- assertEquals(3, getBeansWrapperInstanceCacheSize());
- hardReferences.add(bw3);
- } finally {
- Thread.currentThread().setContextClassLoader(oldTCCL);
- }
-
- {
- BeansWrapperBuilder bwb = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- bwb.setExposeFields(true);
- BeansWrapper bw4 = bwb.build();
- assertEquals(4, getBeansWrapperInstanceCacheSize());
- assertNotSame(bw3, bw4);
- hardReferences.add(bw4);
- }
-
- assertTrue(hardReferences.size() != 0); // just to save it from GC until this line
- }
-
- public void testClassInrospectorCache() throws TemplateModelException {
- assertFalse(new BeansWrapper(Configuration.VERSION_3_0_0).isClassIntrospectionCacheRestricted());
- assertTrue(new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build().isClassIntrospectionCacheRestricted());
-
- ClassIntrospectorBuilder.clearInstanceCache();
- BeansWrapperBuilder.clearInstanceCache();
- checkClassIntrospectorCacheSize(0);
-
- List<BeansWrapper> hardReferences = new LinkedList<>();
- BeansWrapperBuilder factory;
-
- {
- factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
-
- BeansWrapper bw1 = factory.build();
- checkClassIntrospectorCacheSize(1);
-
- factory.setExposureLevel(BeansWrapper.EXPOSE_SAFE); // this was already set to this
- factory.setSimpleMapWrapper(true); // this shouldn't matter for the introspection cache
- BeansWrapper bw2 = factory.build();
- checkClassIntrospectorCacheSize(1);
-
- assertSame(bw2.getClassIntrospector(), bw1.getClassIntrospector());
- assertNotSame(bw1, bw2);
-
- // Wrapping tests:
- assertFalse(exposesFields(bw1));
- assertTrue(exposesProperties(bw1));
- assertTrue(exposesMethods(bw1));
- assertFalse(exposesUnsafe(bw1));
- assertFalse(isSimpleMapWrapper(bw1));
- assertTrue(bw1.isClassIntrospectionCacheRestricted());
- // Prevent introspection cache GC:
- hardReferences.add(bw1);
- hardReferences.add(bw2);
- }
-
- {
- factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- factory.setExposeFields(true);
- BeansWrapper bw = factory.build();
- checkClassIntrospectorCacheSize(2);
- // Wrapping tests:
- assertTrue(exposesFields(bw));
- assertTrue(exposesProperties(bw));
- assertTrue(exposesMethods(bw));
- assertFalse(exposesUnsafe(bw));
- assertFalse(isSimpleMapWrapper(bw));
- // Prevent introspection cache GC:
- hardReferences.add(bw);
- }
-
- {
- factory.setExposureLevel(BeansWrapper.EXPOSE_ALL);
- BeansWrapper bw = factory.build();
- checkClassIntrospectorCacheSize(3);
- // Wrapping tests:
- assertTrue(exposesFields(bw));
- assertTrue(exposesProperties(bw));
- assertTrue(exposesMethods(bw));
- assertTrue(exposesUnsafe(bw));
- assertFalse(isSimpleMapWrapper(bw));
- // Prevent introspection cache GC:
- hardReferences.add(bw);
- }
-
- {
- factory.setExposeFields(false);
- BeansWrapper bw = factory.build();
- checkClassIntrospectorCacheSize(4);
- // Wrapping tests:
- assertFalse(exposesFields(bw));
- assertTrue(exposesProperties(bw));
- assertTrue(exposesMethods(bw));
- assertTrue(exposesUnsafe(bw));
- assertFalse(isSimpleMapWrapper(bw));
- // Prevent introspection cache GC:
- hardReferences.add(bw);
- }
-
- {
- factory.setExposureLevel(BeansWrapper.EXPOSE_NOTHING);
- BeansWrapper bw = factory.build();
- checkClassIntrospectorCacheSize(5);
- // Wrapping tests:
- assertFalse(exposesFields(bw));
- assertFalse(exposesProperties(bw));
- assertFalse(exposesMethods(bw));
- assertFalse(exposesUnsafe(bw));
- assertFalse(isSimpleMapWrapper(bw));
- // Prevent introspection cache GC:
- hardReferences.add(bw);
- }
-
- {
- factory.setExposeFields(true);
- BeansWrapper bw = factory.build();
- checkClassIntrospectorCacheSize(6);
- // Wrapping tests:
- assertTrue(exposesFields(bw));
- assertFalse(exposesProperties(bw));
- assertFalse(exposesMethods(bw));
- assertFalse(exposesUnsafe(bw));
- assertFalse(isSimpleMapWrapper(bw));
- // Prevent introspection cache GC:
- hardReferences.add(bw);
- }
-
- {
- factory.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
- BeansWrapper bw = factory.build();
- checkClassIntrospectorCacheSize(7);
- // Wrapping tests:
- assertTrue(exposesFields(bw));
- assertTrue(exposesProperties(bw));
- assertFalse(exposesMethods(bw));
- assertFalse(exposesUnsafe(bw));
- assertFalse(isSimpleMapWrapper(bw));
- // Prevent introspection cache GC:
- hardReferences.add(bw);
- }
-
- {
- factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- factory.setExposeFields(false);
- factory.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
-
- BeansWrapper bw1 = factory.build();
- checkClassIntrospectorCacheSize(8);
- ClassIntrospector ci1 = bw1.getClassIntrospector();
-
- factory.setSimpleMapWrapper(true); // Shouldn't mater
- BeansWrapper bw2 = factory.build();
- ClassIntrospector ci2 = bw2.getClassIntrospector();
- checkClassIntrospectorCacheSize(8);
-
- assertSame(ci1, ci2);
- assertNotSame(bw1, bw2);
-
- // Wrapping tests:
- assertFalse(exposesFields(bw1));
- assertTrue(exposesProperties(bw1));
- assertFalse(exposesMethods(bw1));
- assertFalse(exposesUnsafe(bw1));
- assertFalse(isSimpleMapWrapper(bw1));
-
- // Prevent introspection cache GC:
- hardReferences.add(bw1);
- hardReferences.add(bw2);
- }
-
- BeansWrapperBuilder.clearInstanceCache(); // otherwise ClassInrospector cache couldn't become cleared in reality
- _ModelImplApi.DefaultObjectWrapperFactory_clearInstanceCache();
- clearClassIntrospectorInstanceCacheReferences(false);
- checkClassIntrospectorCacheSize(8);
- assertEquals(0, getClassIntrospectorNonClearedInstanceCacheSize());
-
- {
- factory.setSimpleMapWrapper(false);
- factory.setExposeFields(false);
-
- BeansWrapper bw1 = factory.build();
- checkClassIntrospectorCacheSize(8);
- assertEquals(1, getClassIntrospectorNonClearedInstanceCacheSize());
- ClassIntrospector ci1 = bw1.getClassIntrospector();
-
- factory.setSimpleMapWrapper(true); // Shouldn't mater
- BeansWrapper bw2 = factory.build();
- ClassIntrospector ci2 = bw2.getClassIntrospector();
-
- assertSame(ci1, ci2);
- assertNotSame(bw1, bw2);
-
- // Wrapping tests:
- assertFalse(exposesFields(bw1));
- assertTrue(exposesProperties(bw1));
- assertFalse(exposesMethods(bw1));
- assertFalse(exposesUnsafe(bw1));
- assertFalse(isSimpleMapWrapper(bw1));
-
- // Prevent introspection cache GC:
- hardReferences.add(bw1);
- hardReferences.add(bw2);
- }
-
- {
- factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- BeansWrapper bw = factory.build();
- checkClassIntrospectorCacheSize(8);
- assertEquals(2, getClassIntrospectorNonClearedInstanceCacheSize());
- // Wrapping tests:
- assertFalse(exposesFields(bw));
- assertTrue(exposesProperties(bw));
- assertTrue(exposesMethods(bw));
- assertFalse(exposesUnsafe(bw));
- // Prevent introspection cache GC:
- hardReferences.add(bw);
- }
-
- clearClassIntrospectorInstanceCacheReferences(true);
- checkClassIntrospectorCacheSize(8);
- assertEquals(0, getClassIntrospectorNonClearedInstanceCacheSize());
-
- {
- factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- factory.setExposeFields(true);
- BeansWrapper bw = factory.build();
- checkClassIntrospectorCacheSize(1);
- // Wrapping tests:
- assertTrue(exposesFields(bw));
- assertTrue(exposesProperties(bw));
- assertTrue(exposesMethods(bw));
- assertFalse(exposesUnsafe(bw));
- // Prevent introspection cache GC:
- hardReferences.add(bw);
- }
-
- {
- factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- factory.setMethodAppearanceFineTuner(new MethodAppearanceFineTuner() {
- @Override
- public void process(MethodAppearanceDecisionInput in, MethodAppearanceDecision out) {
- }
- }); // spoils ClassIntrospector() sharing
-
- BeansWrapper bw1 = factory.build();
- assertSame(bw1, factory.build());
-
- factory.setSimpleMapWrapper(true);
- BeansWrapper bw2 = factory.build();
- checkClassIntrospectorCacheSize(1);
- assertNotSame(bw1, bw2);
- assertNotSame(bw1.getClassIntrospector(), bw2.getClassIntrospector());
- assertTrue(bw1.isClassIntrospectionCacheRestricted());
- assertTrue(bw2.isClassIntrospectionCacheRestricted());
-
- // Wrapping tests:
- assertFalse(exposesFields(bw1));
- assertFalse(exposesFields(bw2));
- assertTrue(exposesProperties(bw1));
- assertTrue(exposesProperties(bw2));
- assertTrue(exposesMethods(bw1));
- assertTrue(exposesMethods(bw2));
- assertFalse(exposesUnsafe(bw1));
- assertFalse(exposesUnsafe(bw2));
- assertFalse(isSimpleMapWrapper(bw1));
- assertTrue(isSimpleMapWrapper(bw2));
- }
-
- {
- factory = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- factory.setMethodAppearanceFineTuner(
- GetlessMethodsAsPropertyGettersRule.INSTANCE); // doesn't spoils sharing
-
- BeansWrapper bw1 = factory.build();
- assertSame(bw1, factory.build());
- checkClassIntrospectorCacheSize(2);
-
- factory.setSimpleMapWrapper(true);
- BeansWrapper bw2 = factory.build();
- checkClassIntrospectorCacheSize(2);
-
- assertNotSame(bw1, bw2);
- assertSame(bw1.getClassIntrospector(), bw2.getClassIntrospector()); // !
- assertTrue(bw1.isClassIntrospectionCacheRestricted());
- assertTrue(bw2.isClassIntrospectionCacheRestricted());
-
- // Wrapping tests:
- assertFalse(exposesFields(bw1));
- assertFalse(exposesFields(bw2));
- assertTrue(exposesProperties(bw1));
- assertTrue(exposesProperties(bw2));
- assertTrue(exposesMethods(bw1));
- assertTrue(exposesMethods(bw2));
- assertFalse(exposesUnsafe(bw1));
- assertFalse(exposesUnsafe(bw2));
- assertFalse(isSimpleMapWrapper(bw1));
- assertTrue(isSimpleMapWrapper(bw2));
- }
-
- assertTrue(hardReferences.size() != 0); // just to save it from GC until this line
- }
-
- private void checkClassIntrospectorCacheSize(int expectedSize) {
- assertEquals(expectedSize, getClassIntrospectorInstanceCacheSize());
- }
-
- private void assertNotEquals(Object o1, Object o2) {
- assertFalse(o1.equals(o2));
- }
-
- public class C {
-
- public String foo = "FOO";
-
- public String getBar() {
- return "BAR";
- }
-
- }
-
- private boolean isSimpleMapWrapper(BeansWrapper bw) throws TemplateModelException {
- return bw.wrap(new HashMap()) instanceof SimpleMapModel;
- }
-
- private boolean exposesFields(BeansWrapper bw) throws TemplateModelException {
- TemplateHashModel thm = (TemplateHashModel) bw.wrap(new C());
- TemplateScalarModel r = (TemplateScalarModel) thm.get("foo");
- if (r == null) return false;
- assertEquals("FOO", r.getAsString());
- return true;
- }
-
- private boolean exposesProperties(BeansWrapper bw) throws TemplateModelException {
- TemplateHashModel thm = (TemplateHashModel) bw.wrap(new C());
- TemplateScalarModel r = (TemplateScalarModel) thm.get("bar");
- if (r == null) return false;
- assertEquals("BAR", r.getAsString());
- return true;
- }
-
- private boolean exposesMethods(BeansWrapper bw) throws TemplateModelException {
- TemplateHashModel thm = (TemplateHashModel) bw.wrap(new C());
- return thm.get("getBar") != null;
- }
-
- private boolean exposesUnsafe(BeansWrapper bw) throws TemplateModelException {
- TemplateHashModel thm = (TemplateHashModel) bw.wrap(new C());
- return thm.get("wait") != null;
- }
-
- static int getClassIntrospectorInstanceCacheSize() {
- Map instanceCache = ClassIntrospectorBuilder.getInstanceCache();
- synchronized (instanceCache) {
- return instanceCache.size();
- }
- }
-
- static int getClassIntrospectorNonClearedInstanceCacheSize() {
- Map instanceCache = ClassIntrospectorBuilder.getInstanceCache();
- synchronized (instanceCache) {
- int cnt = 0;
- for (Iterator it = instanceCache.values().iterator(); it.hasNext(); ) {
- if (((Reference) it.next()).get() != null) cnt++;
- }
- return cnt;
- }
- }
-
- static void clearClassIntrospectorInstanceCacheReferences(boolean enqueue) {
- Map instanceCache = ClassIntrospectorBuilder.getInstanceCache();
- synchronized (instanceCache) {
- for (Iterator it = instanceCache.values().iterator(); it.hasNext(); ) {
- Reference ref = ((Reference) it.next());
- ref.clear();
- if (enqueue) {
- ref.enqueue();
- }
- }
- }
- }
-
- static int getBeansWrapperInstanceCacheSize() {
- Map instanceCache = BeansWrapperBuilder.getInstanceCache();
- synchronized (instanceCache) {
- int size = 0;
- for (Iterator it1 = instanceCache.values().iterator(); it1.hasNext(); ) {
- size += ((Map) it1.next()).size();
- }
- return size;
- }
- }
-
- static int getBeansWrapperNonClearedInstanceCacheSize() {
- Map instanceCache = BeansWrapperBuilder.getInstanceCache();
- synchronized (instanceCache) {
- int cnt = 0;
- for (Iterator it1 = instanceCache.values().iterator(); it1.hasNext(); ) {
- Map tcclScope = (Map) it1.next();
- for (Iterator it2 = tcclScope.values().iterator(); it2.hasNext(); ) {
- if (((Reference) it2.next()).get() != null) cnt++;
- }
- }
- return cnt;
- }
- }
-
- static void clearBeansWrapperInstanceCacheReferences(boolean enqueue) {
- Map instanceCache = BeansWrapperBuilder.getInstanceCache();
- synchronized (instanceCache) {
- for (Iterator it1 = instanceCache.values().iterator(); it1.hasNext(); ) {
- Map tcclScope = (Map) it1.next();
- for (Iterator it2 = tcclScope.values().iterator(); it2.hasNext(); ) {
- Reference ref = ((Reference) it2.next());
- ref.clear();
- if (enqueue) {
- ref.enqueue();
- }
- }
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperWithShortedMethods.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperWithShortedMethods.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperWithShortedMethods.java
deleted file mode 100644
index 1a4ea27..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperWithShortedMethods.java
+++ /dev/null
@@ -1,41 +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 org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.Version;
-
-/**
- * Used so that the order in which the methods are added to the introspection cache is deterministic.
- */
-public abstract class BeansWrapperWithShortedMethods extends BeansWrapper {
-
- public BeansWrapperWithShortedMethods(boolean desc) {
- super(Configuration.VERSION_3_0_0);
- setMethodSorter(new AlphabeticalMethodSorter(desc));
- }
-
- public BeansWrapperWithShortedMethods(Version incompatibleImprovements, boolean desc) {
- super(incompatibleImprovements);
- setMethodSorter(new AlphabeticalMethodSorter(desc));
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/CommonSupertypeForUnwrappingHintTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/CommonSupertypeForUnwrappingHintTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/CommonSupertypeForUnwrappingHintTest.java
deleted file mode 100644
index e7ea63c..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/CommonSupertypeForUnwrappingHintTest.java
+++ /dev/null
@@ -1,129 +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.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, BeansWrapper 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/beans/DefaultObjectWrapperDesc.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/DefaultObjectWrapperDesc.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/DefaultObjectWrapperDesc.java
deleted file mode 100644
index bf61205..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/DefaultObjectWrapperDesc.java
+++ /dev/null
@@ -1,30 +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 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/beans/DefaultObjectWrapperInc.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/DefaultObjectWrapperInc.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/DefaultObjectWrapperInc.java
deleted file mode 100644
index 60a331a..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/DefaultObjectWrapperInc.java
+++ /dev/null
@@ -1,30 +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 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/beans/DefaultObjectWrapperWithSortedMethods.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/DefaultObjectWrapperWithSortedMethods.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/DefaultObjectWrapperWithSortedMethods.java
deleted file mode 100644
index a67aad2..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/DefaultObjectWrapperWithSortedMethods.java
+++ /dev/null
@@ -1,41 +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 org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.Version;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-
-public class DefaultObjectWrapperWithSortedMethods extends DefaultObjectWrapper {
-
- public DefaultObjectWrapperWithSortedMethods(boolean desc) {
- this(Configuration.VERSION_3_0_0, desc);
- }
-
- public DefaultObjectWrapperWithSortedMethods(Version incompatibleImprovements, boolean desc) {
- super(incompatibleImprovements);
- setMethodSorter(this, desc);
- }
-
- static void setMethodSorter(BeansWrapper bw, boolean desc) {
- bw.setMethodSorter(new AlphabeticalMethodSorter(desc));
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/EnumModelsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/EnumModelsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/EnumModelsTest.java
deleted file mode 100644
index 90421c9..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/EnumModelsTest.java
+++ /dev/null
@@ -1,84 +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 static org.junit.Assert.*;
-
-import java.util.ArrayList;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class EnumModelsTest {
-
- @Test
- public void modelCaching() throws Exception {
- BeansWrapper bw = new BeansWrapper(Configuration.VERSION_3_0_0);
- TemplateHashModel enums = bw.getEnumModels();
- TemplateHashModel e = (TemplateHashModel) enums.get(E.class.getName());
- assertNotNull(e);
- assertNotNull(e.get("A"));
- assertNotNull(e.get("B"));
- assertNull(e.get("C"));
-
- try {
- enums.get("no.such.ClassExists");
- fail();
- } catch (TemplateModelException ex) {
- assertTrue(ex.getCause() instanceof ClassNotFoundException);
- }
-
- TemplateModel a = e.get("A");
- assertTrue(a instanceof TemplateScalarModel);
- assertTrue(a instanceof TemplateHashModel);
- assertEquals(((TemplateScalarModel) a).getAsString(), "ts:A");
- TemplateMethodModelEx nameMethod = (TemplateMethodModelEx) ((TemplateHashModel) a).get("name");
- assertEquals(((TemplateScalarModel) nameMethod.exec(new ArrayList())).getAsString(), "A");
-
- assertSame(e, enums.get(E.class.getName()));
-
- bw.clearClassIntrospecitonCache();
- TemplateHashModel eAfterClean = (TemplateHashModel) enums.get(E.class.getName());
- assertNotSame(e, eAfterClean);
- assertSame(eAfterClean, enums.get(E.class.getName()));
- assertNotNull(eAfterClean.get("A"));
- assertNotNull(eAfterClean.get("B"));
- assertNull(eAfterClean.get("C"));
- }
-
- public enum E {
- A, B;
-
- @Override
- public String toString() {
- return "ts:" + super.toString();
- }
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/ErrorMessagesTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/ErrorMessagesTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/ErrorMessagesTest.java
deleted file mode 100644
index 61a2612..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/ErrorMessagesTest.java
+++ /dev/null
@@ -1,170 +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 static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.util.Collections;
-import java.util.Date;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
-import org.apache.freemarker.core.outputformat.impl.TemplateHTMLOutputModel;
-import org.junit.Test;
-
-public class ErrorMessagesTest {
-
- @Test
- public void getterMessage() throws TemplateModelException {
- BeansWrapper bw = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
- TemplateHashModel thm= (TemplateHashModel) bw.wrap(new TestBean());
-
- try {
- thm.get("foo");
- } catch (TemplateModelException e) {
- e.printStackTrace();
- final String msg = e.getMessage();
- assertThat(msg, containsString("\"foo\""));
- assertThat(msg, containsString("existing sub-variable"));
- }
- assertNull(thm.get("bar"));
- }
-
- @Test
- public void markupOutputParameter() throws Exception {
- TemplateHTMLOutputModel html = HTMLOutputFormat.INSTANCE.fromMarkup("<p>a");
-
- BeansWrapper bw = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
- TemplateHashModel thm = (TemplateHashModel) bw.wrap(new TestBean());
-
- {
- TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("m1");
- try {
- m.exec(Collections.singletonList(html));
- fail();
- } catch (TemplateModelException e) {
- assertThat(e.getMessage(), allOf(
- containsString("String"), containsString("convert"), containsString("markup_output"),
- containsString("Tip:"), containsString("?markup_string")));
- }
- }
-
- {
- TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("m2");
- try {
- m.exec(Collections.singletonList(html));
- fail();
- } catch (TemplateModelException e) {
- assertThat(e.getMessage(), allOf(
- containsString("Date"), containsString("convert"), containsString("markup_output"),
- not(containsString("?markup_string"))));
- }
- }
-
- for (String methodName : new String[] { "mOverloaded", "mOverloaded3" }) {
- TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get(methodName);
- try {
- m.exec(Collections.singletonList(html));
- fail();
- } catch (TemplateModelException e) {
- assertThat(e.getMessage(), allOf(
- containsString("No compatible overloaded"),
- containsString("String"), containsString("markup_output"),
- containsString("Tip:"), containsString("?markup_string")));
- }
- }
-
- {
- TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("mOverloaded2");
- try {
- m.exec(Collections.singletonList(html));
- fail();
- } catch (TemplateModelException e) {
- assertThat(e.getMessage(), allOf(
- containsString("No compatible overloaded"),
- containsString("Integer"), containsString("markup_output"),
- not(containsString("?markup_string"))));
- }
- }
-
- {
- TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("mOverloaded4");
- Object r = m.exec(Collections.singletonList(html));
- if (r instanceof TemplateScalarModel) {
- r = ((TemplateScalarModel) r).getAsString();
- }
- assertEquals("<p>a", r);
- }
- }
-
- public static class TestBean {
-
- public String getFoo() {
- throw new RuntimeException("Dummy");
- }
-
- public void m1(String s) {
- // nop
- }
-
- public void m2(Date s) {
- // nop
- }
-
- public void mOverloaded(String s) {
- // nop
- }
-
- public void mOverloaded(Date d) {
- // nop
- }
-
- public void mOverloaded2(Integer n) {
- // nop
- }
-
- public void mOverloaded2(Date d) {
- // nop
- }
-
- public void mOverloaded3(String... s) {
- // nop
- }
-
- public void mOverloaded3(Date d) {
- // nop
- }
-
- public String mOverloaded4(String s) {
- return s;
- }
-
- public String mOverloaded4(TemplateHTMLOutputModel s) throws TemplateModelException {
- return s.getOutputFormat().getMarkupString(s);
- }
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/FineTuneMethodAppearanceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/FineTuneMethodAppearanceTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/FineTuneMethodAppearanceTest.java
deleted file mode 100644
index d21e116..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/FineTuneMethodAppearanceTest.java
+++ /dev/null
@@ -1,65 +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 static org.junit.Assert.*;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class FineTuneMethodAppearanceTest {
-
- @Test
- public void newWayOfConfiguring() throws TemplateModelException {
- DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
- ow.setMethodAppearanceFineTuner(GetlessMethodsAsPropertyGettersRule.INSTANCE);
- ow.setExposeFields(true);
- checkIfProperlyWrapped(ow.wrap(new C()));
- }
-
- private void checkIfProperlyWrapped(TemplateModel tm) throws TemplateModelException {
- TemplateHashModel thm = (TemplateHashModel) tm;
- assertEquals("v1", ((TemplateScalarModel) thm.get("v1")).getAsString());
- assertEquals("v2()", ((TemplateScalarModel) thm.get("v2")).getAsString());
- assertEquals("getV3()", ((TemplateScalarModel) thm.get("v3")).getAsString());
- assertTrue(thm.get("getV3") instanceof TemplateMethodModelEx);
- }
-
- static public class C {
-
- public String v1 = "v1";
-
- public String v2 = "v2";
- public String v2() { return "v2()"; }
-
- public String v3() { return "v3()"; }
- public String getV3() { return "getV3()"; }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/GetlessMethodsAsPropertyGettersRule.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/GetlessMethodsAsPropertyGettersRule.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/GetlessMethodsAsPropertyGettersRule.java
deleted file mode 100644
index 08a52e3..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/GetlessMethodsAsPropertyGettersRule.java
+++ /dev/null
@@ -1,70 +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.beans.IntrospectionException;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.Method;
-
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecision;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecisionInput;
-
-class GetlessMethodsAsPropertyGettersRule implements MethodAppearanceFineTuner, SingletonCustomizer {
-
- static final GetlessMethodsAsPropertyGettersRule INSTANCE = new GetlessMethodsAsPropertyGettersRule();
-
- // Can't be constructed from outside
- private GetlessMethodsAsPropertyGettersRule() { }
-
- @Override
- public void process(
- MethodAppearanceDecisionInput in, MethodAppearanceDecision out) {
- legacyProcess(in.getContainingClass(), in.getMethod(), out);
- }
-
- /** This only exists as the tests need to call this through the deprecated method too. */
- public void legacyProcess(
- Class clazz, Method m, MethodAppearanceDecision decision) {
- if (m.getDeclaringClass() != Object.class
- && m.getReturnType() != void.class
- && m.getParameterTypes().length == 0) {
- String mName = m.getName();
- if (!looksLikePropertyReadMethod(mName)) {
- decision.setExposeMethodAs(null);
- try {
- decision.setExposeAsProperty(new PropertyDescriptor(
- mName, clazz, mName, null));
- } catch (IntrospectionException e) { // Won't happen...
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- private static boolean looksLikePropertyReadMethod(String name) {
- final int verbEnd;
- if (name.startsWith("get")) verbEnd = 3;
- else if (name.startsWith("is")) verbEnd = 2;
- else return false;
-
- return name.length() == verbEnd || Character.isUpperCase(name.charAt(verbEnd));
- }
-
-}
\ 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/beans/IsApplicableTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/IsApplicableTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/IsApplicableTest.java
deleted file mode 100644
index 0bc9168..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/IsApplicableTest.java
+++ /dev/null
@@ -1,171 +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.io.Serializable;
-import java.lang.reflect.Method;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.List;
-
-import junit.framework.TestCase;
-
-@SuppressWarnings("boxing")
-public class IsApplicableTest extends TestCase {
-
- public IsApplicableTest(String name) {
- super(name);
- }
-
- public void testSingle() {
- ArgumentTypes ats = new ArgumentTypes(new Object[] { new Object() });
- assertApplicable(ats, Object.class);
- assertNotApplicable(ats, String.class);
- assertNotApplicable(ats, CharSequence.class);
- assertNotApplicable(ats, Integer.class);
- assertNotApplicable(ats, Integer.TYPE);
-
- ats = new ArgumentTypes(new Object[] { "" });
- assertApplicable(ats, Object.class);
- assertApplicable(ats, CharSequence.class);
- assertApplicable(ats, String.class);
- assertNotApplicable(ats, Integer.class);
- assertNotApplicable(ats, Integer.TYPE);
-
- ats = new ArgumentTypes(new Object[] { 1 });
- assertApplicable(ats, Object.class);
- assertNotApplicable(ats, CharSequence.class);
- assertNotApplicable(ats, String.class);
- assertNotApplicable(ats, Short.class);
- assertNotApplicable(ats, Short.TYPE);
- assertApplicable(ats, Integer.class);
- assertApplicable(ats, Integer.TYPE);
- assertApplicable(ats, Float.class);
- assertApplicable(ats, Float.TYPE);
- assertApplicable(ats, Double.class);
- assertApplicable(ats, Double.TYPE);
- assertApplicable(ats, BigDecimal.class);
- assertApplicable(ats, BigInteger.class);
-
- ats = new ArgumentTypes(new Object[] { new OverloadedNumberUtil.IntegerOrByte(1, (byte) 1) });
- assertApplicable(ats, Object.class);
- assertNotApplicable(ats, CharSequence.class);
- assertNotApplicable(ats, String.class);
- assertApplicable(ats, Short.class);
- assertApplicable(ats, Short.TYPE);
- assertApplicable(ats, Integer.class);
- assertApplicable(ats, Integer.TYPE);
- assertApplicable(ats, Float.class);
- assertApplicable(ats, Float.TYPE);
- assertApplicable(ats, Double.class);
- assertApplicable(ats, Double.TYPE);
- assertApplicable(ats, BigDecimal.class);
- assertApplicable(ats, BigInteger.class);
-
- ats = new ArgumentTypes(new Object[] { 1.0f });
- assertApplicable(ats, Object.class);
- assertNotApplicable(ats, CharSequence.class);
- assertNotApplicable(ats, String.class);
- assertNotApplicable(ats, Integer.class);
- assertNotApplicable(ats, Integer.TYPE);
- assertApplicable(ats, Float.class);
- assertApplicable(ats, Float.TYPE);
- assertApplicable(ats, Double.class);
- assertApplicable(ats, Double.TYPE);
- assertApplicable(ats, BigDecimal.class);
- assertNotApplicable(ats, BigInteger.class);
-
- ats = new ArgumentTypes(new Object[] { null });
- assertApplicable(ats, Object.class);
- assertApplicable(ats, String.class);
- assertApplicable(ats, Integer.class);
- assertNotApplicable(ats, Integer.TYPE);
- assertNotApplicable(ats, Boolean.TYPE);
- assertNotApplicable(ats, Object.class, Object.class);
- assertNotApplicable(ats);
- }
-
- public void testMulti() {
- ArgumentTypes ats = new ArgumentTypes(new Object[] { new Object(), "", 1, true });
- assertApplicable(ats, Object.class, Object.class, Object.class, Object.class);
- assertApplicable(ats, Object.class, String.class, Number.class, Boolean.class);
- assertApplicable(ats, Object.class, CharSequence.class, Integer.class, Serializable.class);
- assertApplicable(ats, Object.class, Comparable.class, Integer.TYPE, Serializable.class);
- assertNotApplicable(ats, Object.class, String.class, Number.class, Number.class);
- assertNotApplicable(ats, Object.class, StringBuilder.class, Number.class, Boolean.class);
- assertNotApplicable(ats, int.class, Object.class, Object.class, Object.class);
- assertNotApplicable(ats, Object.class, Object.class, Object.class);
- assertNotApplicable(ats, Object.class, Object.class, Object.class, Object.class, Object.class);
- }
-
- public void testNoParam() {
- ArgumentTypes ats = new ArgumentTypes(new Object[] { });
- assertApplicable(ats);
- assertNotApplicable(ats, Object.class);
- }
-
- public void testVarags() {
- Object[][] argLists = new Object[][] {
- new Object[] { "", 1, 2, 3 },
- new Object[] { "", 1, (byte) 2, 3 },
- new Object[] { "", 1},
- new Object[] { "" },
- };
- for (Object[] args : argLists) {
- ArgumentTypes ats = new ArgumentTypes(args);
- assertApplicable(ats, true, String.class, int[].class);
- assertApplicable(ats, true, String.class, Integer[].class);
- assertApplicable(ats, true, Object.class, Comparable[].class);
- assertApplicable(ats, true, Object.class, Object[].class);
- assertNotApplicable(ats, true, StringBuilder.class, int[].class);
- if (args.length > 1) {
- assertNotApplicable(ats, true, String.class, String[].class);
- } else {
- assertApplicable(ats, true, String.class, String[].class);
- }
- }
- }
-
- private void assertNotApplicable(ArgumentTypes ats, Class... paramTypes) {
- assertNotApplicable(ats, false, paramTypes);
- }
-
- private void assertNotApplicable(ArgumentTypes ats, boolean varargs, Class... paramTypes) {
- List tested = new ArrayList();
- tested.add(new ReflectionCallableMemberDescriptor((Method) null, paramTypes));
- if (ats.getApplicables(tested, varargs).size() != 0) {
- fail("Parameter types were applicable");
- }
- }
-
- private void assertApplicable(ArgumentTypes ats, Class<?>... paramTypes) {
- assertApplicable(ats, false, paramTypes);
- }
-
- private void assertApplicable(ArgumentTypes ats, boolean varargs, Class<?>... paramTypes) {
- List tested = new ArrayList();
- tested.add(new ReflectionCallableMemberDescriptor((Method) null, paramTypes));
- if (ats.getApplicables(tested, varargs).size() != 1) {
- fail("Parameter types weren't applicable");
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/IsMoreSpecificParameterTypeTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/IsMoreSpecificParameterTypeTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/IsMoreSpecificParameterTypeTest.java
deleted file mode 100644
index e71f071..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/IsMoreSpecificParameterTypeTest.java
+++ /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.
- */
-
-package org.apache.freemarker.core.model.impl.beans;
-
-import java.math.BigDecimal;
-import java.util.HashMap;
-import java.util.Map;
-
-import junit.framework.TestCase;
-
-public class IsMoreSpecificParameterTypeTest extends TestCase {
-
- public IsMoreSpecificParameterTypeTest(String name) {
- super(name);
- }
-
- public void testFixed() {
- assertEquals(1, _MethodUtil.isMoreOrSameSpecificParameterType(String.class, String.class, true, 0));
- assertEquals(1, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, int.class, true, 0));
-
- assertEquals(2, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Integer.class, true, 0));
- assertEquals(2, _MethodUtil.isMoreOrSameSpecificParameterType(boolean.class, Boolean.class, true, 0));
-
- assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, long.class, true, 0));
- assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, double.class, true, 0));
- assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Long.class, true, 0));
- assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Double.class, true, 0));
- assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Long.class, true, 0));
- assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Double.class, true, 0));
-
- assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(HashMap.class, Map.class, true, 0));
- assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(String.class, CharSequence.class, true, 0));
- assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Number.class, true, 0));
- assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Number.class, true, 0));
-
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Map.class, String.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, int.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Boolean.class, boolean.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, boolean.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Boolean.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, String.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, BigDecimal.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Long.class, Integer.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(long.class, Integer.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Long.class, int.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, BigDecimal.class, true, 0));
- }
-
- public void testBuggy() {
- assertEquals(1, _MethodUtil.isMoreOrSameSpecificParameterType(String.class, String.class, false, 0));
- assertEquals(1, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, int.class, false, 0));
-
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Integer.class, false, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(boolean.class, Boolean.class, false, 0));
-
- assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, long.class, false, 0));
- assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, double.class, false, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Long.class, false, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Double.class, false, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Long.class, false, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Double.class, false, 0));
-
- assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(HashMap.class, Map.class, false, 0));
- assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(String.class, CharSequence.class, false, 0));
- assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Number.class, false, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Number.class, false, 0));
-
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Map.class, String.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, int.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Boolean.class, boolean.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, boolean.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Boolean.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, String.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, BigDecimal.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Long.class, Integer.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(long.class, Integer.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Long.class, int.class, true, 0));
- assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, BigDecimal.class, true, 0));
- }
-
-}
[13/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapper.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapper.java
deleted file mode 100644
index 2bc7902..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapper.java
+++ /dev/null
@@ -1,1644 +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.beans.PropertyDescriptor;
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.ResourceBundle;
-import java.util.Set;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.Version;
-import org.apache.freemarker.core._CoreAPI;
-import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core._DelayedFTLTypeDescription;
-import org.apache.freemarker.core._DelayedShortClassName;
-import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.AdapterTemplateModel;
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
-import org.apache.freemarker.core.model.RichObjectWrapper;
-import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelAdapter;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateNumberModel;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.WrapperTemplateModel;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-import org.apache.freemarker.core.util.BugException;
-import org.apache.freemarker.core.util.UndeclaredThrowableException;
-import org.apache.freemarker.core.util.WriteProtectable;
-import org.apache.freemarker.core.util._ClassUtil;
-import org.slf4j.Logger;
-
-/**
- * {@link ObjectWrapper} that is able to expose the Java API of arbitrary Java objects. This is also the superclass of
- * {@link DefaultObjectWrapper}. Note that instances of this class generally should be created with a
- * {@link BeansWrapperBuilder}, not with its public constructors.
- *
- * <p>
- * As of 2.3.22, using {@link BeansWrapper} unextended is not recommended. Instead, {@link DefaultObjectWrapper} with
- * its {@code incompatibleImprovements} property set to 2.3.22 (or higher) is the recommended {@link ObjectWrapper}.
- *
- * <p>
- * 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 {@link Configuration}, of course it's enough if that was safely
- * published and then left unmodified. Using {@link BeansWrapperBuilder} also guarantees thread safety.
- */
-public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
-
- private static final Logger LOG = _CoreLogs.BEANS_WRAPPER;
-
- 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 {@link #setMethodsShadowItems(boolean)} with <tt>false</tt> value to
- * speed up map item retrieval.
- */
- public static final int EXPOSE_NOTHING = 3;
-
- // -----------------------------------------------------------------------------------------------------------------
- // Introspection cache:
-
- private final Object sharedIntrospectionLock;
-
- /**
- * {@link Class} to class info cache.
- * This object is possibly shared with other {@link BeansWrapper}-s!
- *
- * <p>To write this, always use {@link #replaceClassIntrospector(ClassIntrospectorBuilder)}.
- *
- * <p>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;
-
- /**
- * {@link String} class name to {@link StaticModel} cache.
- * This object only belongs to a single {@link BeansWrapper}.
- * This has to be final as {@link #getStaticModels()} might returns it any time and then it has to remain a good
- * reference.
- */
- private final StaticModels staticModels;
-
- /**
- * {@link String} class name to {@link EnumerationModel} cache.
- * This object only belongs to a single {@link BeansWrapper}.
- * This has to be final as {@link #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 {@link 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 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;
-
- /**
- * Use {@link 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
- *
- * @param incompatibleImprovements
- * Sets which of the non-backward-compatible improvements should be enabled. Not {@code null}. This version number
- * is the same as the FreeMarker version number with which the improvements were implemented.
- *
- * <p>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.
- *
- * <p>The reason it's separate from {@link Configuration#setIncompatibleImprovements(Version)} is that
- * {@link ObjectWrapper} objects are often shared among multiple {@link Configuration}-s, so the two version
- * numbers are technically independent. But it's recommended to keep those two version numbers the same.
- *
- * <p>The changes enabled by {@code incompatibleImprovements} are:
- * <ul>
- * <li>
- * <p>2.3.0: No changes; this is the starting point, the version used in older projects.
- * </li>
- * <li>
- * <p>2.3.21 (or higher):
- * Several glitches were oms in <em>overloaded</em> 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 {@code null} arguments were oms, as earlier they were only seen
- * applicable to parameters of type {@code Object}. Now {@code 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 {@code Object} parameter type. For more details
- * about overloaded method selection changes see the version history in the FreeMarker Manual.
- * </li>
- * <li>
- * <p>2.3.24 (or higher):
- * {@link Iterator}-s were always said to be non-empty when using {@code ?has_content} and such (i.e.,
- * operators that check emptiness without reading any elements). Now an {@link Iterator} counts as
- * empty exactly if it has no elements left. (Note that this bug has never affected basic functionality, like
- * {@code <#list ...>}.)
- * </li>
- * </ul>
- *
- * <p>Note that the version will be normalized to the lowest version where the same incompatible
- * {@link BeansWrapper} improvements were already present, so {@link #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.
- }
-
- /**
- * Same as {@link #BeansWrapper(BeansWrapperConfiguration, boolean, boolean)} with {@code true}
- * {@code finalizeConstruction} argument.
- *
- * @since 2.3.21
- */
- protected BeansWrapper(BeansWrapperConfiguration bwConf, boolean writeProtected) {
- this(bwConf, writeProtected, true);
- }
-
- /**
- * Initializes the instance based on the the {@link BeansWrapperConfiguration} specified.
- *
- * @param writeProtected Makes the instance's configuration settings read-only via
- * {@link WriteProtectable#writeProtect()}; this way it can use the shared class introspection cache.
- *
- * @param finalizeConstruction Decides if the construction is finalized now, or the caller will do some more
- * adjustments on the instance and then call {@link #finalizeConstruction(boolean)} itself.
- *
- * @since 2.3.22
- */
- protected BeansWrapper(BeansWrapperConfiguration bwConf, boolean writeProtected, boolean finalizeConstruction) {
- incompatibleImprovements = bwConf.getIncompatibleImprovements(); // normalized
-
- simpleMapWrapper = bwConf.isSimpleMapWrapper();
- defaultDateType = bwConf.getDefaultDateType();
- outerIdentity = bwConf.getOuterIdentity() != null ? bwConf.getOuterIdentity() : this;
- strict = 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.
- sharedIntrospectionLock = new Object();
- classIntrospector = new ClassIntrospector(bwConf.classIntrospectorFactory, sharedIntrospectionLock);
- } 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.
- classIntrospector = bwConf.classIntrospectorFactory.build();
- sharedIntrospectionLock = classIntrospector.getSharedLock();
- }
-
- falseModel = new BooleanModel(Boolean.FALSE, this);
- trueModel = new BooleanModel(Boolean.TRUE, this);
-
- staticModels = new StaticModels(BeansWrapper.this);
- enumModels = createEnumModels(BeansWrapper.this);
- modelCache = new BeansModelCache(BeansWrapper.this);
- setUseModelCache(bwConf.getUseModelCache());
-
- finalizeConstruction(writeProtected);
- }
-
- /**
- * Meant to be called after {@link BeansWrapper#BeansWrapper(BeansWrapperConfiguration, boolean, boolean)} when
- * its last argument was {@code 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
- * {@code writeProtected} was {@code 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 {@link 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.
- *
- * <p>Consider using {@link BeansWrapperBuilder} instead, which gives an instance that's already
- * write protected and also uses some shared caches/pools.
- *
- * @since 2.3.21
- */
- @Override
- public void writeProtect() {
- writeProtected = true;
- }
-
- /**
- * @since 2.3.21
- */
- @Override
- public boolean isWriteProtected() {
- return writeProtected;
- }
-
- Object getSharedIntrospectionLock() {
- return sharedIntrospectionLock;
- }
-
- /**
- * If this object is already read-only according to {@link WriteProtectable}, throws {@link IllegalStateException},
- * otherwise does nothing.
- *
- * @since 2.3.21
- */
- protected void checkModifiable() {
- if (writeProtected) throw new IllegalStateException(
- "Can't modify the " + getClass().getName() + " object, as it was write protected.");
- }
-
- /**
- * @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 you are doing.
- */
- public void setStrict(boolean strict) {
- checkModifiable();
- 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) {
- checkModifiable();
- this.outerIdentity = outerIdentity;
- }
-
- /**
- * By default returns <tt>this</tt>.
- * @see #setOuterIdentity(ObjectWrapper)
- */
- public ObjectWrapper getOuterIdentity() {
- return outerIdentity;
- }
-
- /**
- * When set to {@code true}, the keys in {@link Map}-s won't mix with the method names when looking at them
- * from templates. The default is {@code false} for backward-compatibility, but is not recommended.
- *
- * <p>When this is {@code false}, {@code myMap.foo} or {@code myMap['foo']} either returns the method {@code foo},
- * or calls {@code Map.get("foo")}. If both exists (the method and the {@link Map} key), one will hide the other,
- * depending on the {@link #isMethodsShadowItems()}, which default to {@code true} (the method
- * wins). Some frameworks use this so that you can call {@code 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
- * {@link Map} entries (or the other way around). Thus, this setup is not recommended.
- * (Technical note: {@link Map}-s will be wrapped into {@link MapModel} in this case.)
- *
- * <p>When this is {@code true}, {@code myMap.foo} or {@code myMap['foo']} always calls {@code Map.get("foo")}.
- * The methods of the {@link Map} object aren't visible from templates in this case. This, however, spoils the
- * {@code myMap.get(nonStringKey)} workaround. But now you can use {@code myMap(nonStringKey)} instead, that is, you
- * can use the map itself as the {@code get} method.
- * (Technical note: {@link Map}-s will be wrapped into {@link SimpleMapModel} in this case.)
- *
- * <p>*: For historical reasons, FreeMarker 2.3.X doesn't support non-string keys with the {@code []} operator,
- * hence the workarounds. This will be likely oms in FreeMarker 2.4.0. Also note that the method- and
- * the "field"-namespaces aren't separate in FreeMarker, hence {@code myMap.get} can return the {@code get}
- * method.
- */
- public void setSimpleMapWrapper(boolean simpleMapWrapper) {
- checkModifiable();
- this.simpleMapWrapper = simpleMapWrapper;
- }
-
- /**
- * Tells whether Maps are exposed as simple maps, without access to their
- * method. See {@link #setSimpleMapWrapper(boolean)} for details.
- * @return true if Maps are exposed as simple hashes, false if they're
- * exposed as full JavaBeans.
- */
- public boolean isSimpleMapWrapper() {
- return simpleMapWrapper;
- }
-
- // 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 <code>EXPOSE_SAFE</code>.
- * @param exposureLevel can be any of the <code>EXPOSE_xxx</code>
- * constants.
- */
- public void setExposureLevel(int exposureLevel) {
- checkModifiable();
-
- if (classIntrospector.getExposureLevel() != exposureLevel) {
- ClassIntrospectorBuilder pa = classIntrospector.getPropertyAssignments();
- pa.setExposureLevel(exposureLevel);
- replaceClassIntrospector(pa);
- }
- }
-
- /**
- * @since 2.3.21
- */
- public int getExposureLevel() {
- return classIntrospector.getExposureLevel();
- }
-
- /**
- * Controls whether public instance fields of classes are exposed to
- * templates.
- * @param 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 (classIntrospector.getExposeFields() != exposeFields) {
- ClassIntrospectorBuilder pa = classIntrospector.getPropertyAssignments();
- pa.setExposeFields(exposeFields);
- replaceClassIntrospector(pa);
- }
- }
-
- /**
- * Returns whether exposure of public instance fields of classes is
- * enabled. See {@link #setExposeFields(boolean)} for details.
- * @return true if public instance fields are exposed, false otherwise.
- */
- public boolean isExposeFields() {
- return classIntrospector.getExposeFields();
- }
-
- public MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
- return classIntrospector.getMethodAppearanceFineTuner();
- }
-
- /**
- * Used to tweak certain aspects of how methods appear in the data-model;
- * see {@link MethodAppearanceFineTuner} for more.
- */
- public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner methodAppearanceFineTuner) {
- checkModifiable();
-
- if (classIntrospector.getMethodAppearanceFineTuner() != methodAppearanceFineTuner) {
- ClassIntrospectorBuilder pa = classIntrospector.getPropertyAssignments();
- pa.setMethodAppearanceFineTuner(methodAppearanceFineTuner);
- replaceClassIntrospector(pa);
- }
- }
-
- MethodSorter getMethodSorter() {
- return classIntrospector.getMethodSorter();
- }
-
- void setMethodSorter(MethodSorter methodSorter) {
- checkModifiable();
-
- if (classIntrospector.getMethodSorter() != methodSorter) {
- ClassIntrospectorBuilder pa = classIntrospector.getPropertyAssignments();
- pa.setMethodSorter(methodSorter);
- replaceClassIntrospector(pa);
- }
- }
-
- /**
- * Tells if this instance acts like if its class introspection cache is sharable with other {@link BeansWrapper}-s.
- * A restricted cache denies certain too "antisocial" operations, like {@link #clearClassIntrospecitonCache()}.
- * The value depends on how the instance
- * was created; with a public constructor (then this is {@code false}), or with {@link BeansWrapperBuilder}
- * (then it's {@code 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 {@code true} even then.
- *
- * @since 2.3.21
- */
- public boolean isClassIntrospectionCacheRestricted() {
- return classIntrospector.getHasSharedInstanceRestrictons();
- }
-
- /**
- * Replaces the value of {@link #classIntrospector}, but first it unregisters
- * the model factories in the old {@link #classIntrospector}.
- */
- private void replaceClassIntrospector(ClassIntrospectorBuilder pa) {
- checkModifiable();
-
- final ClassIntrospector newCI = new ClassIntrospector(pa, sharedIntrospectionLock);
- 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 (sharedIntrospectionLock) {
- oldCI = classIntrospector;
- if (oldCI != null) {
- // Note that after unregistering the model factory might still gets some callback from the old
- // classIntrospector
- if (staticModels != null) {
- oldCI.unregisterModelFactory(staticModels);
- staticModels.clearCache();
- }
- if (enumModels != null) {
- oldCI.unregisterModelFactory(enumModels);
- enumModels.clearCache();
- }
- if (modelCache != null) {
- oldCI.unregisterModelFactory(modelCache);
- modelCache.clearCache();
- }
- if (trueModel != null) {
- trueModel.clearMemberCache();
- }
- if (falseModel != null) {
- falseModel.clearMemberCache();
- }
- }
-
- classIntrospector = newCI;
-
- registerModelFactories();
- }
- }
-
- private void registerModelFactories() {
- if (staticModels != null) {
- classIntrospector.registerModelFactory(staticModels);
- }
- if (enumModels != null) {
- classIntrospector.registerModelFactory(enumModels);
- }
- if (modelCache != null) {
- classIntrospector.registerModelFactory(modelCache);
- }
- }
-
- /**
- * Sets whether methods shadow items in beans. When true (this is the
- * default value), <code>${object.name}</code> 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
- * <code>object.get(name)</code>, 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 = methodsShadowItems;
- }
- }
-
- boolean isMethodsShadowItems() {
- return methodsShadowItems;
- }
-
- /**
- * Sets the default date type to use for date models that result from
- * a plain <tt>java.util.Date</tt> instead of <tt>java.sql.Date</tt> or
- * <tt>java.sql.Time</tt> or <tt>java.sql.Timestamp</tt>. Default value is
- * {@link TemplateDateModel#UNKNOWN}.
- * @param 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 = defaultDateType;
- }
- }
-
- /**
- * Returns the default date type. See {@link #setDefaultDateType(int)} for
- * details.
- * @return the default date type
- */
- public int getDefaultDateType() {
- return defaultDateType;
- }
-
- /**
- * Sets whether this wrapper caches the {@link TemplateModel}-s created for the Java objects that has wrapped with
- * this object wrapper. Default is {@code false}.
- * When set to {@code true}, calling {@link #wrap(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 setUseModelCache(boolean useCache) {
- checkModifiable();
- modelCache.setUseCache(useCache);
- }
-
- /**
- * @since 2.3.21
- */
- public boolean getUseModelCache() {
- return modelCache.getUseCache();
- }
-
- /**
- * Returns the version given with {@link #BeansWrapper(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 incompatibleImprovements;
- }
-
- /**
- * Returns the lowest version number that is equivalent with the parameter version.
- * @since 2.3.21
- */
- protected static Version normalizeIncompatibleImprovementsVersion(Version incompatibleImprovements) {
- _CoreAPI.checkVersionNotNullAndSupported(incompatibleImprovements);
- return Configuration.VERSION_3_0_0;
- }
-
- /**
- * Wraps the object with a template model that is most specific for the object's
- * class. Specifically:
- * <ul>
- * <li>if the object is a Number returns a {@link NumberModel} for it,</li>
- * <li>if the object is a Date returns a {@link DateModel} for it,</li>
- * <li>if the object is a Boolean returns
- * {@link org.apache.freemarker.core.model.TemplateBooleanModel#TRUE} or
- * {@link org.apache.freemarker.core.model.TemplateBooleanModel#FALSE}</li>
- * <li>if the object is already a TemplateModel, returns it unchanged,</li>
- * <li>if the object is an array, returns a {@link ArrayModel} for it
- * <li>if the object is a Map, returns a {@link MapModel} for it
- * <li>if the object is a Collection, returns a {@link CollectionModel} for it
- * <li>if the object is an Iterator, returns a {@link IteratorModel} for it
- * <li>if the object is an Enumeration, returns a {@link EnumerationModel} for it
- * <li>if the object is a String, returns a {@link StringModel} for it
- * <li>otherwise, returns a generic {@link StringModel} for it.
- * </ul>
- */
- @Override
- public TemplateModel wrap(Object object) throws TemplateModelException {
- if (object == null) return null;
- return modelCache.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 {@link 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 {@link TemplateMethodModelEx} that selects an
- * overload each time it's called based on the argument values.
- *
- * @param object The object whose method will be called, or {@code null} if {@code method} is a static method.
- * This object will be used "as is", like without unwrapping it if it's a {@link TemplateModelAdapter}.
- * @param method The method to call, which must be an (inherited) member of the class of {@code object}, as
- * described by {@link Method#invoke(Object, Object...)}
- *
- * @since 2.3.22
- */
- public TemplateMethodModelEx wrap(Object object, Method method) {
- return new SimpleMethodModel(object, method, method.getParameterTypes(), this);
- }
-
- /**
- * @since 2.3.22
- */
- @Override
- public TemplateHashModel wrapAsAPI(Object obj) throws TemplateModelException {
- return new APIModel(obj, this);
- }
-
- private final ModelFactory BOOLEAN_FACTORY = new ModelFactory() {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return ((Boolean) object).booleanValue() ? trueModel : falseModel;
- }
- };
-
- private static final ModelFactory ITERATOR_FACTORY = new ModelFactory() {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new IteratorModel((Iterator<?>) object, (BeansWrapper) wrapper);
- }
- };
-
- private static final ModelFactory ENUMERATION_FACTORY = new ModelFactory() {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new EnumerationModel((Enumeration<?>) object, (BeansWrapper) wrapper);
- }
- };
-
- protected ModelFactory getModelFactory(Class<?> clazz) {
- if (Map.class.isAssignableFrom(clazz)) {
- return simpleMapWrapper ? SimpleMapModel.FACTORY : MapModel.FACTORY;
- }
- if (Collection.class.isAssignableFrom(clazz)) {
- return CollectionModel.FACTORY;
- }
- if (Number.class.isAssignableFrom(clazz)) {
- return NumberModel.FACTORY;
- }
- if (Date.class.isAssignableFrom(clazz)) {
- return DateModel.FACTORY;
- }
- if (Boolean.class == clazz) { // Boolean is final
- return BOOLEAN_FACTORY;
- }
- if (ResourceBundle.class.isAssignableFrom(clazz)) {
- return ResourceBundleModel.FACTORY;
- }
- if (Iterator.class.isAssignableFrom(clazz)) {
- return ITERATOR_FACTORY;
- }
- if (Enumeration.class.isAssignableFrom(clazz)) {
- return ENUMERATION_FACTORY;
- }
- if (clazz.isArray()) {
- return ArrayModel.FACTORY;
- }
- return StringModel.FACTORY;
- }
-
- /**
- * Attempts to unwrap a model into underlying object. Generally, this
- * method is the inverse of the {@link #wrap(Object)} method. In addition
- * it will unwrap arbitrary {@link TemplateNumberModel} instances into
- * a number, arbitrary {@link TemplateDateModel} instances into a date,
- * {@link TemplateScalarModel} instances into a String, arbitrary
- * {@link TemplateBooleanModel} instances into a Boolean, arbitrary
- * {@link TemplateHashModel} instances into a Map, arbitrary
- * {@link TemplateSequenceModel} into a List, and arbitrary
- * {@link TemplateCollectionModel} into a Set. All other objects are
- * returned unchanged.
- * @throws TemplateModelException if an attempted unwrapping fails.
- */
- @Override
- public Object unwrap(TemplateModel model) throws TemplateModelException {
- return unwrap(model, Object.class);
- }
-
- /**
- * Attempts to unwrap a model into an object of the desired class.
- * Generally, this method is the inverse of the {@link #wrap(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.
- * @param model the model to unwrap
- * @param targetClass the class of the unwrapped result; {@code Object.class} of we don't know what the expected type is.
- * @return the unwrapped result of the desired class
- * @throws TemplateModelException if an attempted unwrapping fails.
- *
- * @see #tryUnwrapTo(TemplateModel, Class)
- */
- public Object unwrap(TemplateModel model, Class<?> targetClass)
- throws TemplateModelException {
- final Object obj = tryUnwrapTo(model, targetClass);
- if (obj == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
- throw new TemplateModelException("Can not unwrap model of type " +
- model.getClass().getName() + " to type " + targetClass.getName());
- }
- return obj;
- }
-
- /**
- * @since 2.3.22
- */
- @Override
- public Object tryUnwrapTo(TemplateModel model, Class<?> targetClass) throws TemplateModelException {
- return tryUnwrapTo(model, targetClass, 0);
- }
-
- /**
- * @param typeFlags
- * Used when unwrapping for overloaded methods and so the {@code targetClass} is possibly too generic
- * (as it's the most specific common superclass). Must be 0 when unwrapping parameter values for
- * non-overloaded methods.
- * @return {@link ObjectWrapperAndUnwrapper#CANT_UNWRAP_TO_TARGET_CLASS} or the unwrapped object.
- */
- Object tryUnwrapTo(TemplateModel model, Class<?> targetClass, int typeFlags) throws TemplateModelException {
- Object res = tryUnwrapTo(model, targetClass, typeFlags, null);
- if ((typeFlags & TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT) != 0
- && res instanceof Number) {
- return OverloadedNumberUtil.addFallbackType((Number) res, typeFlags);
- } else {
- return res;
- }
- }
-
- /**
- * See {@link #tryUnwrapTo(TemplateModel, Class, int)}.
- */
- private Object tryUnwrapTo(final TemplateModel model, Class<?> targetClass, final int typeFlags,
- final Map<Object, Object> recursionStops)
- throws TemplateModelException {
- if (model == null) {
- return null;
- }
-
- if (targetClass.isPrimitive()) {
- targetClass = _ClassUtil.primitiveClassToBoxingClass(targetClass);
- }
-
- // This is for transparent interop with other wrappers (and ourselves)
- // Passing the targetClass allows e.g. a Jython-aware method that declares a
- // PyObject as its argument to receive a PyObject from a Jython-aware TemplateModel
- // 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((Number) wrapped, targetClass);
- if (number != null) return 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((Number) wrapped, targetClass);
- 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) {
-
- // [2.4][IcI]: Should also 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 ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
- }
-
- // Primitive numeric types & Number.class and its subclasses
- if (_ClassUtil.isNumerical(targetClass)) {
- if (model instanceof TemplateNumberModel) {
- Number number = forceUnwrappedNumberToType(
- ((TemplateNumberModel) model).getAsNumber(), targetClass);
- if (number != null) {
- return number;
- }
- }
- }
-
- if (boolean.class == targetClass || Boolean.class == targetClass) {
- if (model instanceof TemplateBooleanModel) {
- return Boolean.valueOf(((TemplateBooleanModel) model).getAsBoolean());
- }
- // Boolean is final, no other conversion will work
- return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
- }
-
- if (Map.class == targetClass) {
- if (model instanceof TemplateHashModel) {
- return new HashAdapter((TemplateHashModel) model, this);
- }
- }
-
- if (List.class == targetClass) {
- if (model instanceof TemplateSequenceModel) {
- return new SequenceAdapter((TemplateSequenceModel) model, this);
- }
- }
-
- if (Set.class == targetClass) {
- if (model instanceof TemplateCollectionModel) {
- return new SetAdapter((TemplateCollectionModel) model, this);
- }
- }
-
- if (Collection.class == targetClass || Iterable.class == targetClass) {
- if (model instanceof TemplateCollectionModel) {
- return new CollectionAdapter((TemplateCollectionModel) model,
- this);
- }
- if (model instanceof TemplateSequenceModel) {
- return new SequenceAdapter((TemplateSequenceModel) model, this);
- }
- }
-
- // TemplateSequenceModels can be converted to arrays
- if (targetClass.isArray()) {
- if (model instanceof TemplateSequenceModel) {
- return unwrapSequenceToArray((TemplateSequenceModel) model, targetClass, true, recursionStops);
- }
- // array classes are final, no other conversion will work
- return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
- }
-
- // 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 Character.valueOf(s.charAt(0));
- }
- }
- // Character is final, no other conversion will work
- return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
- }
-
- 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 & TypeFlags.ACCEPTS_NUMBER) != 0)
- && model instanceof TemplateNumberModel) {
- Number number = ((TemplateNumberModel) model).getAsNumber();
- if (itf != 0 || targetClass.isInstance(number)) {
- return number;
- }
- }
- if ((itf == 0 || (itf & TypeFlags.ACCEPTS_DATE) != 0)
- && model instanceof TemplateDateModel) {
- Date date = ((TemplateDateModel) model).getAsDate();
- if (itf != 0 || targetClass.isInstance(date)) {
- return date;
- }
- }
- if ((itf == 0 || (itf & (TypeFlags.ACCEPTS_STRING | TypeFlags.CHARACTER)) != 0)
- && model instanceof TemplateScalarModel
- && (itf != 0 || targetClass.isAssignableFrom(String.class))) {
- String strVal = ((TemplateScalarModel) model).getAsString();
- if (itf == 0 || (itf & TypeFlags.CHARACTER) == 0) {
- return strVal;
- } else { // TypeFlags.CHAR == 1
- if (strVal.length() == 1) {
- if ((itf & TypeFlags.ACCEPTS_STRING) != 0) {
- return new CharacterOrString(strVal);
- } else {
- return Character.valueOf(strVal.charAt(0));
- }
- } else if ((itf & TypeFlags.ACCEPTS_STRING) != 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 & TypeFlags.ACCEPTS_BOOLEAN) != 0)
- && model instanceof TemplateBooleanModel
- && (itf != 0 || targetClass.isAssignableFrom(Boolean.class))) {
- return Boolean.valueOf(((TemplateBooleanModel) model).getAsBoolean());
- }
- if ((itf == 0 || (itf & TypeFlags.ACCEPTS_MAP) != 0)
- && model instanceof TemplateHashModel
- && (itf != 0 || targetClass.isAssignableFrom(HashAdapter.class))) {
- return new HashAdapter((TemplateHashModel) model, this);
- }
- if ((itf == 0 || (itf & TypeFlags.ACCEPTS_LIST) != 0)
- && model instanceof TemplateSequenceModel
- && (itf != 0 || targetClass.isAssignableFrom(SequenceAdapter.class))) {
- return new SequenceAdapter((TemplateSequenceModel) model, this);
- }
- if ((itf == 0 || (itf & TypeFlags.ACCEPTS_SET) != 0)
- && model instanceof TemplateCollectionModel
- && (itf != 0 || targetClass.isAssignableFrom(SetAdapter.class))) {
- return new SetAdapter((TemplateCollectionModel) model, this);
- }
-
- if ((itf & TypeFlags.ACCEPTS_ARRAY) != 0
- && model instanceof TemplateSequenceModel) {
- return new SequenceAdapter((TemplateSequenceModel) model, this);
- }
-
- 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;
- }
-
- return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
- }
-
- /**
- * @param tryOnly
- * If {@code true}, if the conversion of an item to the component type isn't possible, the method returns
- * {@link ObjectWrapperAndUnwrapper#CANT_UNWRAP_TO_TARGET_CLASS} instead of throwing a
- * {@link TemplateModelException}.
- */
- Object unwrapSequenceToArray(
- TemplateSequenceModel seq, Class<?> arrayClass, boolean tryOnly, Map<Object, Object> 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(componentType, seq.size());
- recursionStops.put(seq, array);
- try {
- final int size = seq.size();
- for (int i = 0; i < size; i++) {
- final TemplateModel seqItem = seq.get(i);
- Object val = tryUnwrapTo(seqItem, componentType, 0, recursionStops);
- if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
- if (tryOnly) {
- return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
- } else {
- throw new _TemplateModelException(
- "Failed to convert ", new _DelayedFTLTypeDescription(seq),
- " object to ", new _DelayedShortClassName(array.getClass()),
- ": Problematic sequence item at index ", Integer.valueOf(i) ," with value type: ",
- new _DelayedFTLTypeDescription(seqItem));
- }
-
- }
- Array.set(array, i, val);
- }
- } finally {
- recursionStops.remove(seq);
- }
- return array;
- }
-
- Object listToArray(List<?> list, Class<?> arrayClass, Map<Object, Object> recursionStops)
- throws TemplateModelException {
- if (list instanceof SequenceAdapter) {
- return unwrapSequenceToArray(
- ((SequenceAdapter) list).getTemplateSequenceModel(),
- arrayClass, false,
- 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(componentType, list.size());
- recursionStops.put(list, array);
- try {
- boolean isComponentTypeExamined = false;
- boolean isComponentTypeNumerical = false; // will be filled on demand
- boolean isComponentTypeList = false; // will be filled on demand
- int i = 0;
- for (Object listItem : list) {
- 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((Number) listItem, componentType);
- } else if (componentType == String.class && listItem instanceof Character) {
- listItem = String.valueOf(((Character) listItem).charValue());
- } else if ((componentType == Character.class || componentType == char.class)
- && listItem instanceof String) {
- String listItemStr = (String) listItem;
- if (listItemStr.length() == 1) {
- listItem = Character.valueOf(listItemStr.charAt(0));
- }
- } else if (componentType.isArray()) {
- if (listItem instanceof List) {
- listItem = listToArray((List<?>) listItem, componentType, recursionStops);
- } else if (listItem instanceof TemplateSequenceModel) {
- listItem = unwrapSequenceToArray((TemplateSequenceModel) listItem, componentType, false, recursionStops);
- }
- } else if (isComponentTypeList && listItem.getClass().isArray()) {
- listItem = arrayToList(listItem);
- }
- }
- try {
- Array.set(array, i, listItem);
- } 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;
- }
-
- /**
- * @param array Must be an array (of either a reference or primitive type)
- */
- List<?> arrayToList(Object array) throws 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 ? Collections.EMPTY_LIST : new NonPrimitiveArrayBackedReadOnlyList(objArray);
- } else {
- // Array of any primitive type
- return Array.getLength(array) == 0 ? Collections.EMPTY_LIST : new PrimtiveArrayBackedReadOnlyList(array);
- }
- }
-
- /**
- * Converts a number to the target type aggressively (possibly with overflow or significant loss of precision).
- * @param n Non-{@code null}
- * @return {@code null} if the conversion has failed.
- */
- static Number forceUnwrappedNumberToType(final Number n, final Class<?> targetType) {
- // 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 ? (Integer) n : Integer.valueOf(n.intValue());
- } else if (targetType == long.class || targetType == Long.class) {
- return n instanceof Long ? (Long) n : Long.valueOf(n.longValue());
- } else if (targetType == double.class || targetType == Double.class) {
- return n instanceof Double ? (Double) n : Double.valueOf(n.doubleValue());
- } else if (targetType == BigDecimal.class) {
- if (n instanceof BigDecimal) {
- return n;
- } else if (n instanceof BigInteger) {
- return new BigDecimal((BigInteger) n);
- } 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 ? (Float) n : Float.valueOf(n.floatValue());
- } else if (targetType == byte.class || targetType == Byte.class) {
- return n instanceof Byte ? (Byte) n : Byte.valueOf(n.byteValue());
- } else if (targetType == short.class || targetType == Short.class) {
- return n instanceof Short ? (Short) n : Short.valueOf(n.shortValue());
- } else if (targetType == BigInteger.class) {
- if (n instanceof BigInteger) {
- return n;
- } else {
- if (n instanceof OverloadedNumberUtil.IntegerBigDecimal) {
- return ((OverloadedNumberUtil.IntegerBigDecimal) n).bigIntegerValue();
- } else if (n instanceof BigDecimal) {
- return ((BigDecimal) n).toBigInteger();
- } else {
- return BigInteger.valueOf(n.longValue());
- }
- }
- } else {
- final Number oriN = n instanceof OverloadedNumberUtil.NumberWithFallbackType
- ? ((OverloadedNumberUtil.NumberWithFallbackType) n).getSourceNumber() : n;
- if (targetType.isInstance(oriN)) {
- // Handle nonstandard Number subclasses as well as directly java.lang.Number.
- return oriN;
- } else {
- // Fails
- return null;
- }
- }
- }
-
- /**
- * Invokes the specified method, wrapping the return value. The specialty
- * of this method is that if the return value is null, and the return type
- * of the invoked method is void, {@link TemplateModel#NOTHING} is returned.
- * @param object the object to invoke the method on
- * @param method the method to invoke
- * @param args the arguments to the method
- * @return the wrapped return value of the method.
- * @throws InvocationTargetException if the invoked method threw an exception
- * @throws IllegalAccessException if the method can't be invoked due to an
- * access restriction.
- * @throws TemplateModelException if the return value couldn't be wrapped
- * (this can happen if the wrapper has an outer identity or is subclassed,
- * and the outer identity or the subclass throws an exception. Plain
- * BeansWrapper never throws TemplateModelException).
- */
- TemplateModel invokeMethod(Object object, Method method, Object[] args)
- throws InvocationTargetException,
- IllegalAccessException,
- TemplateModelException {
- // [2.4]: Java's Method.invoke truncates numbers if the target type has not enough bits to hold the value.
- // There should at least be an option to check this.
- Object retval = method.invoke(object, args);
- return
- method.getReturnType() == void.class
- ? TemplateModel.NOTHING
- : getOuterIdentity().wrap(retval);
- }
-
- /**
- * Returns a hash model that represents the so-called class static models.
- * Every class static model is itself a hash through which you can call
- * static methods on the specified class. To obtain a static model for a
- * class, get the element of this hash with the fully qualified class name.
- * For example, if you place this hash model inside the root data model
- * under name "statics", you can use i.e. <code>statics["java.lang.
- * System"]. currentTimeMillis()</code> to call the {@link
- * java.lang.System#currentTimeMillis()} method.
- * @return a hash model whose keys are fully qualified class names, and
- * that returns hash models whose elements are the static models of the
- * classes.
- */
- public TemplateHashModel getStaticModels() {
- return staticModels;
- }
-
- /**
- * Returns a hash model that represents the so-called class enum models.
- * Every class' enum model is itself a hash through which you can access
- * enum value declared by the specified class, assuming that class is an
- * enumeration. To obtain an enum model for a class, get the element of this
- * hash with the fully qualified class name. For example, if you place this
- * hash model inside the root data model under name "enums", you can use
- * i.e. <code>statics["java.math.RoundingMode"].UP</code> to access the
- * {@link java.math.RoundingMode#UP} value.
- * @return a hash model whose keys are fully qualified class names, and
- * that returns hash models whose elements are the enum models of the
- * classes.
- * @throws UnsupportedOperationException if this method is invoked on a
- * pre-1.5 JRE, as Java enums aren't supported there.
- */
- public TemplateHashModel getEnumModels() {
- if (enumModels == null) {
- throw new UnsupportedOperationException(
- "Enums not supported before J2SE 5.");
- }
- return enumModels;
- }
-
- /** For Unit tests only */
- ModelCache getModelCache() {
- return modelCache;
- }
-
- /**
- * Creates a new instance of the specified class using the method call logic of this object wrapper for calling the
- * constructor. Overloaded constructors and varargs are supported. Only public constructors will be called.
- *
- * @param clazz The class whose constructor we will call.
- * @param arguments The list of {@link TemplateModel}-s to pass to the constructor after unwrapping them
- * @return The instance created; it's not wrapped into {@link TemplateModel}.
- */
- public Object newInstance(Class<?> clazz, List/*<? extends TemplateModel>*/ arguments)
- throws TemplateModelException {
- try {
- Object ctors = classIntrospector.get(clazz).get(ClassIntrospector.CONSTRUCTORS_KEY);
- if (ctors == null) {
- throw new TemplateModelException("Class " + clazz.getName() +
- " has no public constructors.");
- }
- Constructor<?> ctor = null;
- Object[] objargs;
- if (ctors instanceof SimpleMethod) {
- SimpleMethod sm = (SimpleMethod) ctors;
- ctor = (Constructor<?>) sm.getMember();
- objargs = sm.unwrapArguments(arguments, this);
- try {
- return ctor.newInstance(objargs);
- } catch (Exception e) {
- if (e instanceof TemplateModelException) throw (TemplateModelException) e;
- throw _MethodUtil.newInvocationTemplateModelException(null, ctor, e);
- }
- } else if (ctors instanceof OverloadedMethods) {
- final MemberAndArguments mma = ((OverloadedMethods) ctors).getMemberAndArguments(arguments, this);
- try {
- return mma.invokeConstructor(this);
- } catch (Exception e) {
- if (e instanceof TemplateModelException) throw (TemplateModelException) e;
-
- throw _MethodUtil.newInvocationTemplateModelException(null, mma.getCallableMemberDescriptor(), e);
- }
- } else {
- // Cannot happen
- throw new BugException();
- }
- } catch (TemplateModelException e) {
- throw e;
- } catch (Exception e) {
- throw new TemplateModelException(
- "Error while creating new instance of class " + clazz.getName() + "; see cause exception", e);
- }
- }
-
- /**
- * Removes the introspection data for a class from the cache.
- * Use this if you know that a class is not used anymore in templates.
- * If the class will be still used, the cache entry will be silently
- * re-created, so this isn't a dangerous operation.
- *
- * @since 2.3.20
- */
- public void removeFromClassIntrospectionCache(Class<?> clazz) {
- classIntrospector.remove(clazz);
- }
-
- /**
- * Removes all class introspection data from the cache.
- *
- * <p>Use this if you want to free up memory on the expense of recreating
- * the cache entries for the classes that will be used later in templates.
- *
- * @throws IllegalStateException if {@link #isClassIntrospectionCacheRestricted()} is {@code true}.
- *
- * @since 2.3.20
- */
- public void clearClassIntrospecitonCache() {
- classIntrospector.clearCache();
- }
-
- ClassIntrospector getClassIntrospector() {
- return classIntrospector;
- }
-
- /**
- * Converts any {@link BigDecimal}s in the passed array to the type of
- * the corresponding formal argument of the method.
- */
- // Unused?
- public static void coerceBigDecimals(AccessibleObject callable, Object[] args) {
- Class<?>[] formalTypes = null;
- for (int i = 0; i < args.length; ++i) {
- Object arg = args[i];
- if (arg instanceof BigDecimal) {
- if (formalTypes == null) {
- if (callable instanceof Method) {
- formalTypes = ((Method) callable).getParameterTypes();
- } else if (callable instanceof Constructor) {
- formalTypes = ((Constructor<?>) callable).getParameterTypes();
- } else {
- throw new IllegalArgumentException("Expected method or "
- + " constructor; callable is " +
- callable.getClass().getName());
- }
- }
- args[i] = coerceBigDecimal((BigDecimal) arg, formalTypes[i]);
- }
- }
- }
-
- /**
- * Converts any {@link BigDecimal}-s in the passed array to the type of
- * the corresponding formal argument of the method via {@link #coerceBigDecimal(BigDecimal, Class)}.
- */
- public static void coerceBigDecimals(Class<?>[] formalTypes, Object[] args) {
- int typeLen = formalTypes.length;
- int argsLen = args.length;
- int min = Math.min(typeLen, argsLen);
- for (int i = 0; i < min; ++i) {
- Object arg = args[i];
- if (arg instanceof BigDecimal) {
- args[i] = coerceBigDecimal((BigDecimal) arg, formalTypes[i]);
- }
- }
- if (argsLen > typeLen) {
- Class<?> varArgType = formalTypes[typeLen - 1];
- for (int i = typeLen; i < argsLen; ++i) {
- Object arg = args[i];
- if (arg instanceof BigDecimal) {
- args[i] = coerceBigDecimal((BigDecimal) arg, varArgType);
- }
- }
- }
- }
-
- /**
- * Converts {@link BigDecimal} to the class given in the {@code formalType} argument if that's a known numerical
- * type, returns the {@link BigDecimal} as is otherwise. Overflow and precision loss are possible, similarly as
- * with casting in Java.
- */
- public static Object coerceBigDecimal(BigDecimal bd, Class<?> formalType) {
- // int is expected in most situations, so we check it first
- if (formalType == int.class || formalType == Integer.class) {
- return Integer.valueOf(bd.intValue());
- } else if (formalType == double.class || formalType == Double.class) {
- return Double.valueOf(bd.doubleValue());
- } else if (formalType == long.class || formalType == Long.class) {
- return Long.valueOf(bd.longValue());
- } else if (formalType == float.class || formalType == Float.class) {
- return Float.valueOf(bd.floatValue());
- } else if (formalType == short.class || formalType == Short.class) {
- return Short.valueOf(bd.shortValue());
- } else if (formalType == byte.class || formalType == Byte.class) {
- return Byte.valueOf(bd.byteValue());
- } else if (java.math.BigInteger.class.isAssignableFrom(formalType)) {
- return bd.toBigInteger();
- } else {
- return bd;
- }
- }
-
- /**
- * Returns the exact class name and the identity hash, also the values of the most often used {@link BeansWrapper}
- * configuration properties, also if which (if any) shared class introspection cache it uses.
- *
- * @since 2.3.21
- */
- @Override
- public String toString() {
- final String propsStr = toPropertiesString();
- return _ClassUtil.getShortClassNameOfObject(this) + "@" + System.identityHashCode(this)
- + "(" + incompatibleImprovements + ", "
- + (propsStr.length() != 0 ? propsStr + ", ..." : "")
- + ")";
- }
-
- /**
- * Returns the name-value pairs that describe the configuration of this {@link BeansWrapper}; called from
- * {@link #toString()}. The expected format is like {@code "foo=bar, baaz=wombat"}. When overriding this, you should
- * call the super method, and then insert the content before it with a following {@code ", "}, or after it with a
- * preceding {@code ", "}.
- *
- * @since 2.3.22
- */
- protected String toPropertiesString() {
- // Start with "simpleMapWrapper", because the override in DefaultObjectWrapper expects it to be there!
- return "simpleMapWrapper=" + simpleMapWrapper + ", "
- + "exposureLevel=" + classIntrospector.getExposureLevel() + ", "
- + "exposeFields=" + classIntrospector.getExposeFields() + ", "
- + "sharedClassIntrospCache="
- + (classIntrospector.isShared() ? "@" + System.identityHashCode(classIntrospector) : "none");
- }
-
- private static ClassBasedModelFactory createEnumModels(BeansWrapper wrapper) {
- if (ENUMS_MODEL_CTOR != null) {
- try {
- return (ClassBasedModelFactory) ENUMS_MODEL_CTOR.newInstance(
- wrapper);
- } catch (Exception e) {
- throw new UndeclaredThrowableException(e);
- }
- } else {
- return null;
- }
- }
-
- private static Constructor enumsModelCtor() {
- try {
- // Check if Enums are available on this platform
- Class.forName("java.lang.Enum");
- // If they are, return the appropriate constructor for enum models
- return Class.forName(
- "org.apache.freemarker.core.model.impl.beans._EnumModels").getDeclaredConstructor(
- BeansWrapper.class);
- } catch (Exception e) {
- // Otherwise, return null
- return null;
- }
- }
-
- /**
- * Used for
- * {@link MethodAppearanceFineTuner#process}
- * to store the results; see there.
- */
- static public final class MethodAppearanceDecision {
- private PropertyDescriptor exposeAsProperty;
- private String exposeMethodAs;
- private boolean methodShadowsProperty;
-
- void setDefaults(Method m) {
- exposeAsProperty = null;
- exposeMethodAs = m.getName();
- methodShadowsProperty = true;
- }
-
- public PropertyDescriptor getExposeAsProperty() {
- return exposeAsProperty;
- }
-
- public void setExposeAsProperty(PropertyDescriptor exposeAsProperty) {
- this.exposeAsProperty = exposeAsProperty;
- }
-
- public String getExposeMethodAs() {
- return exposeMethodAs;
- }
-
- public void setExposeMethodAs(String exposeMethodAs) {
- this.exposeMethodAs = exposeMethodAs;
- }
-
- public boolean getMethodShadowsProperty() {
- return methodShadowsProperty;
- }
-
- public void setMethodShadowsProperty(boolean methodShadowsProperty) {
- this.methodShadowsProperty = methodShadowsProperty;
- }
-
- }
-
- /**
- * Used for {@link MethodAppearanceFineTuner#process} as input parameter; see there.
- */
- static public final class MethodAppearanceDecisionInput {
- private Method method;
- private Class<?> containingClass;
-
- void setMethod(Method method) {
- this.method = method;
- }
-
- void setContainingClass(Class<?> containingClass) {
- this.containingClass = containingClass;
- }
-
- public Method getMethod() {
- return method;
- }
-
- public Class/*<?>*/ getContainingClass() {
- return containingClass;
- }
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBuilder.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBuilder.java
deleted file mode 100644
index 2c0dcc0..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperBuilder.java
+++ /dev/null
@@ -1,159 +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.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.Version;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
-
-/**
- * Gets/creates a {@link BeansWrapper} singleton instance that's already configured as specified in the properties of
- * this object; this is recommended over using the {@link BeansWrapper} constructors. The returned instance can't be
- * further configured (it's write protected).
- *
- * <p>The builder meant to be used as a drop-away object (not stored in a field), like in this example:
- * <pre>
- * BeansWrapper beansWrapper = new BeansWrapperBuilder(Configuration.VERSION_2_3_21).build();
- * </pre>
- *
- * <p>Or, a more complex example:</p>
- * <pre>
- * // Create the builder:
- * BeansWrapperBuilder builder = new BeansWrapperBuilder(Configuration.VERSION_2_3_21);
- * // Set desired BeansWrapper configuration properties:
- * builder.setUseModelCache(true);
- * builder.setExposeFields(true);
- *
- * // Get the singleton:
- * BeansWrapper beansWrapper = builder.build();
- * // You don't need the builder anymore.
- * </pre>
- *
- * <p>Despite that builders aren't meant to be used as long-lived objects (singletons), the builder is thread-safe after
- * you have stopped calling its setters and it was safely published (see JSR 133) to other threads. This can be useful
- * if you have to put the builder into an IoC container, rather than the singleton it produces.
- *
- * <p>The main benefit of using a builder instead of a {@link BeansWrapper} constructor is that this way the
- * internal object wrapping-related caches (most notably the class introspection cache) will come from a global,
- * JVM-level (more precisely, {@code freemarker.jar}-class-loader-level) cache. Also the {@link BeansWrapper} singletons
- * themselves are stored in this global cache. Some of the wrapping-related caches are expensive to build and can take
- * significant amount of memory. Using builders, components that use FreeMarker will share {@link BeansWrapper}
- * instances and said caches even if they use separate FreeMarker {@link Configuration}-s. (Many Java libraries use
- * FreeMarker internally, so {@link Configuration} sharing is not an option.)
- *
- * <p>Note that the returned {@link BeansWrapper} instances are only weak-referenced from inside the builder mechanism,
- * so singletons are garbage collected when they go out of usage, just like non-singletons.
- *
- * <p>About the object wrapping-related caches:
- * <ul>
- * <li><p>Class introspection cache: Stores information about classes that once had to be wrapped. The cache is
- * stored in the static fields of certain FreeMarker classes. Thus, if you have two {@link BeansWrapper}
- * instances, they might share the same class introspection cache. But if you have two
- * {@code freemarker.jar}-s (typically, in two Web Application's {@code WEB-INF/lib} directories), those won't
- * share their caches (as they don't share the same FreeMarker classes).
- * Also, currently there's a separate cache for each permutation of the property values that influence class
- * introspection: {@link BeansWrapperBuilder#setExposeFields(boolean) expose_fields} and
- * {@link BeansWrapperBuilder#setExposureLevel(int) exposure_level}. So only {@link BeansWrapper} where those
- * properties are the same may share class introspection caches among each other.
- * </li>
- * <li><p>Model caches: These are local to a {@link BeansWrapper}. {@link BeansWrapperBuilder} returns the same
- * {@link BeansWrapper} instance for equivalent properties (unless the existing instance was garbage collected
- * and thus a new one had to be created), hence these caches will be re-used too. {@link BeansWrapper} instances
- * are cached in the static fields of FreeMarker too, but there's a separate cache for each
- * Thread Context Class Loader, which in a servlet container practically means a separate cache for each Web
- * Application (each servlet context). (This is like so because for resolving class names to classes FreeMarker
- * uses the Thread Context Class Loader, so the result of the resolution can be different for different
- * Thread Context Class Loaders.) The model caches are:
- * <ul>
- * <li><p>
- * Static model caches: These are used by the hash returned by {@link BeansWrapper#getEnumModels()} and
- * {@link BeansWrapper#getStaticModels()}, for caching {@link TemplateModel}-s for the static methods/fields
- * and Java enums that were accessed through them. To use said hashes, you have to put them
- * explicitly into the data-model or expose them to the template explicitly otherwise, so in most applications
- * these caches aren't unused.
- * </li>
- * <li><p>
- * Instance model cache: By default off (see {@link BeansWrapper#setUseModelCache(boolean)}). Caches the
- * {@link TemplateModel}-s for all Java objects that were accessed from templates.
- * </li>
- * </ul>
- * </li>
- * </ul>
- *
- * <p>Note that what this method documentation says about {@link BeansWrapper} also applies to
- * {@link DefaultObjectWrapperBuilder}.
- *
- * @since 2.3.21
- */
-public class BeansWrapperBuilder extends BeansWrapperConfiguration {
-
- private final static Map<ClassLoader, Map<BeansWrapperConfiguration, WeakReference<BeansWrapper>>>
- INSTANCE_CACHE = new WeakHashMap<>();
- private final static ReferenceQueue<BeansWrapper> INSTANCE_CACHE_REF_QUEUE = new ReferenceQueue<>();
-
- private static class BeansWrapperFactory
- implements _BeansAPI._BeansWrapperSubclassFactory<BeansWrapper, BeansWrapperConfiguration> {
-
- private static final BeansWrapperFactory INSTANCE = new BeansWrapperFactory();
-
- @Override
- public BeansWrapper create(BeansWrapperConfiguration bwConf) {
- return new BeansWrapper(bwConf, true);
- }
-
- }
-
- /**
- * See {@link BeansWrapperConfiguration#BeansWrapperConfiguration(Version)}.
- */
- public BeansWrapperBuilder(Version incompatibleImprovements) {
- super(incompatibleImprovements);
- }
-
- /** For unit testing only */
- static void clearInstanceCache() {
- synchronized (INSTANCE_CACHE) {
- INSTANCE_CACHE.clear();
- }
- }
-
- /**
- * For unit testing only
- */
- static Map<ClassLoader, Map<BeansWrapperConfiguration, WeakReference<BeansWrapper>>> getInstanceCache() {
- return INSTANCE_CACHE;
- }
-
- /**
- * Returns a {@link BeansWrapper} instance that matches the settings of this builder. This will be possibly a
- * singleton that is also in use elsewhere, not necessarily a new object.
- */
- public BeansWrapper build() {
- return _BeansAPI.getBeansWrapperSubclassSingleton(
- this, INSTANCE_CACHE, INSTANCE_CACHE_REF_QUEUE, BeansWrapperFactory.INSTANCE);
- }
-
-}
\ No newline at end of file
[10/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsSubset.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsSubset.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsSubset.java
deleted file mode 100644
index 8f41200..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsSubset.java
+++ /dev/null
@@ -1,402 +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.io.Serializable;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.util._ClassUtil;
-import org.apache.freemarker.core.util._NullArgumentException;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-/**
- * Encapsulates the rules and data structures (including cache) for choosing of the best matching callable member for
- * a parameter list, from a given set of callable members. There are two subclasses of this, one for non-varags methods,
- * and one for varargs methods.
- */
-abstract class OverloadedMethodsSubset {
-
- /**
- * Used for an optimization trick to substitute an array of whatever size that contains only 0-s. Since this array
- * is 0 long, this means that the code that reads the int[] always have to check if the int[] has this value, and
- * then it has to act like if was all 0-s.
- */
- static final int[] ALL_ZEROS_ARRAY = new int[0];
-
- private static final int[][] ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY = new int[1][];
- static {
- ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY[0] = ALL_ZEROS_ARRAY;
- }
-
- private Class[/*number of args*/][/*arg index*/] unwrappingHintsByParamCount;
-
- /**
- * Tells what types occur at a given parameter position with a bit field. See {@link TypeFlags}.
- */
- private int[/*number of args*/][/*arg index*/] typeFlagsByParamCount;
-
- // TODO: This can cause memory-leak when classes are re-loaded. However, first the genericClassIntrospectionCache
- // and such need to be oms in this regard.
- private final Map/*<ArgumentTypes, MaybeEmptyCallableMemberDescriptor>*/ argTypesToMemberDescCache
- = new ConcurrentHashMap(6, 0.75f, 1);
-
- private final List/*<ReflectionCallableMemberDescriptor>*/ memberDescs = new LinkedList();
-
- OverloadedMethodsSubset() {
- //
- }
-
- void addCallableMemberDescriptor(ReflectionCallableMemberDescriptor memberDesc) {
- memberDescs.add(memberDesc);
-
- // Warning: Do not modify this array, or put it into unwrappingHintsByParamCount by reference, as the arrays
- // inside that are modified!
- final Class[] prepedParamTypes = preprocessParameterTypes(memberDesc);
- final int paramCount = prepedParamTypes.length; // Must be the same as the length of the original param list
-
- // Merge these unwrapping hints with the existing table of hints:
- if (unwrappingHintsByParamCount == null) {
- unwrappingHintsByParamCount = new Class[paramCount + 1][];
- unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone();
- } else if (unwrappingHintsByParamCount.length <= paramCount) {
- Class[][] newUnwrappingHintsByParamCount = new Class[paramCount + 1][];
- System.arraycopy(unwrappingHintsByParamCount, 0, newUnwrappingHintsByParamCount, 0,
- unwrappingHintsByParamCount.length);
- unwrappingHintsByParamCount = newUnwrappingHintsByParamCount;
- unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone();
- } else {
- Class[] unwrappingHints = unwrappingHintsByParamCount[paramCount];
- if (unwrappingHints == null) {
- unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone();
- } else {
- for (int paramIdx = 0; paramIdx < prepedParamTypes.length; paramIdx++) {
- // For each parameter list length, we merge the argument type arrays into a single Class[] that
- // stores the most specific common types for each position. Hence we will possibly use a too generic
- // hint for the unwrapping. For correct behavior, for each overloaded methods its own parameter
- // types should be used as a hint. But without unwrapping the arguments, we couldn't select the
- // overloaded method. So we had to unwrap with all possible target types of each parameter position,
- // which would be slow and its result would be uncacheable (as we don't have anything usable as
- // a lookup key). So we just use this compromise.
- unwrappingHints[paramIdx] = getCommonSupertypeForUnwrappingHint(
- unwrappingHints[paramIdx], prepedParamTypes[paramIdx]);
- }
- }
- }
-
- int[] typeFlagsByParamIdx = ALL_ZEROS_ARRAY;
- // Fill typeFlagsByParamCount (if necessary)
- for (int paramIdx = 0; paramIdx < paramCount; paramIdx++) {
- final int typeFlags = TypeFlags.classToTypeFlags(prepedParamTypes[paramIdx]);
- if (typeFlags != 0) {
- if (typeFlagsByParamIdx == ALL_ZEROS_ARRAY) {
- typeFlagsByParamIdx = new int[paramCount];
- }
- typeFlagsByParamIdx[paramIdx] = typeFlags;
- }
- }
- mergeInTypesFlags(paramCount, typeFlagsByParamIdx);
-
- afterWideningUnwrappingHints(prepedParamTypes, typeFlagsByParamIdx);
- }
-
- Class[][] getUnwrappingHintsByParamCount() {
- return unwrappingHintsByParamCount;
- }
-
- @SuppressFBWarnings(value="JLM_JSR166_UTILCONCURRENT_MONITORENTER",
- justification="Locks for member descriptor creation only")
- final MaybeEmptyCallableMemberDescriptor getMemberDescriptorForArgs(Object[] args, boolean varArg) {
- ArgumentTypes argTypes = new ArgumentTypes(args);
- MaybeEmptyCallableMemberDescriptor memberDesc
- = (MaybeEmptyCallableMemberDescriptor) argTypesToMemberDescCache.get(argTypes);
- if (memberDesc == null) {
- // Synchronized so that we won't unnecessarily create the same member desc. for multiple times in parallel.
- synchronized (argTypesToMemberDescCache) {
- memberDesc = (MaybeEmptyCallableMemberDescriptor) argTypesToMemberDescCache.get(argTypes);
- if (memberDesc == null) {
- memberDesc = argTypes.getMostSpecific(memberDescs, varArg);
- argTypesToMemberDescCache.put(argTypes, memberDesc);
- }
- }
- }
- return memberDesc;
- }
-
- Iterator/*<ReflectionCallableMemberDescriptor>*/ getMemberDescriptors() {
- return memberDescs.iterator();
- }
-
- abstract Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc);
- abstract void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes);
-
- abstract MaybeEmptyMemberAndArguments getMemberAndArguments(List/*<TemplateModel>*/ tmArgs,
- BeansWrapper unwrapper) throws TemplateModelException;
-
- /**
- * Returns the most specific common class (or interface) of two parameter types for the purpose of unwrapping.
- * This is trickier than finding the most specific overlapping superclass of two classes, because:
- * <ul>
- * <li>It considers primitive classes as the subclasses of the boxing classes.</li>
- * <li>If the only common class is {@link Object}, it will try to find a common interface. If there are more
- * of them, it will start removing those that are known to be uninteresting as unwrapping hints.</li>
- * </ul>
- *
- * @param c1 Parameter type 1
- * @param c2 Parameter type 2
- */
- protected Class getCommonSupertypeForUnwrappingHint(Class c1, Class c2) {
- if (c1 == c2) return c1;
- // This also means that the hint for (Integer, Integer) will be Integer, not just Number. This is consistent
- // with how non-overloaded method hints work.
-
- // c1 primitive class to boxing class:
- final boolean c1WasPrim;
- if (c1.isPrimitive()) {
- c1 = _ClassUtil.primitiveClassToBoxingClass(c1);
- c1WasPrim = true;
- } else {
- c1WasPrim = false;
- }
-
- // c2 primitive class to boxing class:
- final boolean c2WasPrim;
- if (c2.isPrimitive()) {
- c2 = _ClassUtil.primitiveClassToBoxingClass(c2);
- c2WasPrim = true;
- } else {
- c2WasPrim = false;
- }
-
- if (c1 == c2) {
- // If it was like int and Integer, boolean and Boolean, etc., we return the boxing type (as that's the
- // less specific, because it allows null.)
- // (If it was two equivalent primitives, we don't get here, because of the 1st line of the method.)
- return c1;
- } else if (Number.class.isAssignableFrom(c1) && Number.class.isAssignableFrom(c2)) {
- // We don't want the unwrapper to convert to a numerical super-type [*] as it's not yet known what the
- // actual number type of the chosen method will be. We will postpone the actual numerical conversion
- // until that, especially as some conversions (like oms point to floating point) can be lossy.
- // * Numerical super-type: Like long > int > short > byte.
- return Number.class;
- } else if (c1WasPrim || c2WasPrim) {
- // At this point these all stand:
- // - At least one of them was primitive
- // - No more than one of them was numerical
- // - They don't have the same wrapper (boxing) class
- return Object.class;
- }
-
- // We never get to this point if buxfixed is true and any of these stands:
- // - One of classes was a primitive type
- // - One of classes was a numerical type (either boxing type or primitive)
-
- Set commonTypes = _MethodUtil.getAssignables(c1, c2);
- commonTypes.retainAll(_MethodUtil.getAssignables(c2, c1));
- if (commonTypes.isEmpty()) {
- // Can happen when at least one of the arguments is an interface, as
- // they don't have Object at the root of their hierarchy
- return Object.class;
- }
-
- // Gather maximally specific elements. Yes, there can be more than one
- // because of interfaces. I.e., if you call this method for String.class
- // and Number.class, you'll have Comparable, Serializable, and Object as
- // maximal elements.
- List max = new ArrayList();
- listCommonTypes: for (Iterator commonTypesIter = commonTypes.iterator(); commonTypesIter.hasNext(); ) {
- Class clazz = (Class) commonTypesIter.next();
- for (Iterator maxIter = max.iterator(); maxIter.hasNext(); ) {
- Class maxClazz = (Class) maxIter.next();
- if (_MethodUtil.isMoreOrSameSpecificParameterType(maxClazz, clazz, false /*bugfixed [1]*/, 0) != 0) {
- // clazz can't be maximal, if there's already a more specific or equal maximal than it.
- continue listCommonTypes;
- }
- if (_MethodUtil.isMoreOrSameSpecificParameterType(clazz, maxClazz, false /*bugfixed [1]*/, 0) != 0) {
- // If it's more specific than a currently maximal element,
- // that currently maximal is no longer a maximal.
- maxIter.remove();
- }
- // 1: We don't use bugfixed at the "[1]"-marked points because it's slower and doesn't make any
- // difference here as it's ensured that nor c1 nor c2 is primitive or numerical. The bugfix has only
- // affected the treatment of primitives and numerical types.
- }
- // If we get here, no current maximal is more specific than the
- // current class, so clazz is a new maximal so far.
- max.add(clazz);
- }
-
- if (max.size() > 1) { // we have an ambiguity
- // Find the non-interface class
- for (Iterator it = max.iterator(); it.hasNext(); ) {
- Class maxCl = (Class) it.next();
- if (!maxCl.isInterface()) {
- if (maxCl != Object.class) { // This actually shouldn't ever happen, but to be sure...
- // If it's not Object, we use it as the most specific
- return maxCl;
- } else {
- // Otherwise remove Object, and we will try with the interfaces
- it.remove();
- }
- }
- }
-
- // At this point we only have interfaces left.
- // Try removing interfaces about which we know that they are useless as unwrapping hints:
- max.remove(Cloneable.class);
- if (max.size() > 1) { // Still have an ambiguity...
- max.remove(Serializable.class);
- if (max.size() > 1) { // Still had an ambiguity...
- max.remove(Comparable.class);
- if (max.size() > 1) {
- return Object.class; // Still had an ambiguity... no luck.
- }
- }
- }
- }
-
- return (Class) max.get(0);
- }
-
- /**
- * Gets the "type flags" of each parameter positions, or {@code null} if there's no method with this parameter
- * count or if we are in pre-2.3.21 mode, or {@link #ALL_ZEROS_ARRAY} if there were no parameters that turned
- * on a flag. The returned {@code int}-s are one or more {@link TypeFlags} constants binary "or"-ed together.
- */
- final protected int[] getTypeFlags(int paramCount) {
- return typeFlagsByParamCount != null && typeFlagsByParamCount.length > paramCount
- ? typeFlagsByParamCount[paramCount]
- : null;
- }
-
- /**
- * Updates the content of the {@link #typeFlagsByParamCount} field with the parameter type flags of a method.
- *
- * @param dstParamCount The parameter count for which we want to merge in the type flags
- * @param srcTypeFlagsByParamIdx If shorter than {@code dstParamCount}, its last item will be repeated until
- * dstParamCount length is reached. If longer, the excessive items will be ignored.
- * Maybe {@link #ALL_ZEROS_ARRAY}. Maybe a 0-length array. Can't be {@code null}.
- */
- final protected void mergeInTypesFlags(int dstParamCount, int[] srcTypeFlagsByParamIdx) {
- _NullArgumentException.check("srcTypesFlagsByParamIdx", srcTypeFlagsByParamIdx);
-
- // Special case of 0 param count:
- if (dstParamCount == 0) {
- if (typeFlagsByParamCount == null) {
- typeFlagsByParamCount = ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY;
- } else if (typeFlagsByParamCount != ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY) {
- typeFlagsByParamCount[0] = ALL_ZEROS_ARRAY;
- }
- return;
- }
-
- // Ensure that typesFlagsByParamCount[dstParamCount] exists:
- if (typeFlagsByParamCount == null) {
- typeFlagsByParamCount = new int[dstParamCount + 1][];
- } else if (typeFlagsByParamCount.length <= dstParamCount) {
- int[][] newTypeFlagsByParamCount = new int[dstParamCount + 1][];
- System.arraycopy(typeFlagsByParamCount, 0, newTypeFlagsByParamCount, 0,
- typeFlagsByParamCount.length);
- typeFlagsByParamCount = newTypeFlagsByParamCount;
- }
-
- int[] dstTypeFlagsByParamIdx = typeFlagsByParamCount[dstParamCount];
- if (dstTypeFlagsByParamIdx == null) {
- // This is the first method added with this number of params => no merging
-
- if (srcTypeFlagsByParamIdx != ALL_ZEROS_ARRAY) {
- int srcParamCount = srcTypeFlagsByParamIdx.length;
- dstTypeFlagsByParamIdx = new int[dstParamCount];
- for (int paramIdx = 0; paramIdx < dstParamCount; paramIdx++) {
- dstTypeFlagsByParamIdx[paramIdx]
- = srcTypeFlagsByParamIdx[paramIdx < srcParamCount ? paramIdx : srcParamCount - 1];
- }
- } else {
- dstTypeFlagsByParamIdx = ALL_ZEROS_ARRAY;
- }
-
- typeFlagsByParamCount[dstParamCount] = dstTypeFlagsByParamIdx;
- } else {
- // dstTypeFlagsByParamIdx != null, so we need to merge into it.
-
- if (srcTypeFlagsByParamIdx == dstTypeFlagsByParamIdx) {
- // Used to occur when both are ALL_ZEROS_ARRAY
- return;
- }
-
- // As we will write dstTypeFlagsByParamIdx, it can't remain ALL_ZEROS_ARRAY anymore.
- if (dstTypeFlagsByParamIdx == ALL_ZEROS_ARRAY && dstParamCount > 0) {
- dstTypeFlagsByParamIdx = new int[dstParamCount];
- typeFlagsByParamCount[dstParamCount] = dstTypeFlagsByParamIdx;
- }
-
- for (int paramIdx = 0; paramIdx < dstParamCount; paramIdx++) {
- final int srcParamTypeFlags;
- if (srcTypeFlagsByParamIdx != ALL_ZEROS_ARRAY) {
- int srcParamCount = srcTypeFlagsByParamIdx.length;
- srcParamTypeFlags = srcTypeFlagsByParamIdx[paramIdx < srcParamCount ? paramIdx : srcParamCount - 1];
- } else {
- srcParamTypeFlags = 0;
- }
-
- final int dstParamTypesFlags = dstTypeFlagsByParamIdx[paramIdx];
- if (dstParamTypesFlags != srcParamTypeFlags) {
- int mergedTypeFlags = dstParamTypesFlags | srcParamTypeFlags;
- if ((mergedTypeFlags & TypeFlags.MASK_ALL_NUMERICALS) != 0) {
- // Must not be set if we don't have numerical type at this index!
- mergedTypeFlags |= TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT;
- }
- dstTypeFlagsByParamIdx[paramIdx] = mergedTypeFlags;
- }
- }
- }
- }
-
- protected void forceNumberArgumentsToParameterTypes(
- Object[] args, Class[] paramTypes, int[] typeFlagsByParamIndex) {
- final int paramTypesLen = paramTypes.length;
- final int argsLen = args.length;
- for (int argIdx = 0; argIdx < argsLen; argIdx++) {
- final int paramTypeIdx = argIdx < paramTypesLen ? argIdx : paramTypesLen - 1;
- final int typeFlags = typeFlagsByParamIndex[paramTypeIdx];
-
- // Forcing the number type can only be interesting if there are numerical parameter types on that index,
- // and the unwrapping was not to an exact numerical type.
- if ((typeFlags & TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT) != 0) {
- final Object arg = args[argIdx];
- // If arg isn't a number, we can't do any conversions anyway, regardless of the param type.
- if (arg instanceof Number) {
- final Class targetType = paramTypes[paramTypeIdx];
- final Number convertedArg = BeansWrapper.forceUnwrappedNumberToType((Number) arg, targetType);
- if (convertedArg != null) {
- args[argIdx] = convertedArg;
- }
- }
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedNumberUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedNumberUtil.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedNumberUtil.java
deleted file mode 100644
index 4d4ae97..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedNumberUtil.java
+++ /dev/null
@@ -1,1289 +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.math.BigDecimal;
-import java.math.BigInteger;
-
-import org.apache.freemarker.core.model.TemplateNumberModel;
-import org.apache.freemarker.core.util._ClassUtil;
-import org.apache.freemarker.core.util._NumberUtil;
-
-/**
- * Everything related to coercion to ambiguous numerical types.
- */
-class OverloadedNumberUtil {
-
- // Can't be instantiated
- private OverloadedNumberUtil() { }
-
- /**
- * The lower limit of conversion prices where there's a risk of significant mantissa loss.
- * The value comes from misc/overloadedNumberRules/prices.ods and generator.ftl.
- */
- static final int BIG_MANTISSA_LOSS_PRICE = 4 * 10000;
-
- /** The highest long that can be stored in double without precision loss: 2**53. */
- private static final long MAX_DOUBLE_OR_LONG = 9007199254740992L;
- /** The lowest long that can be stored in double without precision loss: -(2**53). */
- private static final long MIN_DOUBLE_OR_LONG = -9007199254740992L;
- private static final int MAX_DOUBLE_OR_LONG_LOG_2 = 53;
-
- /** The highest long that can be stored in float without precision loss: 2**24. */
- private static final int MAX_FLOAT_OR_INT = 16777216;
- /** The lowest long that can be stored in float without precision loss: -(2**24). */
- private static final int MIN_FLOAT_OR_INT = -16777216;
- private static final int MAX_FLOAT_OR_INT_LOG_2 = 24;
- /** Lowest number that we don't thread as possible integer 0. */
- private static final double LOWEST_ABOVE_ZERO = 0.000001;
- /** Highest number that we don't thread as possible integer 1. */
- private static final double HIGHEST_BELOW_ONE = 0.999999;
-
- /**
- * Attaches the lowest alternative number type to the parameter number via {@link NumberWithFallbackType}, if
- * that's useful according the possible target number types. This transformation is applied on the method call
- * argument list before overloaded method selection.
- *
- * <p>Note that as of this writing, this method is only used when
- * {@link BeansWrapper#getIncompatibleImprovements()} >= 2.3.21.
- *
- * <p>Why's this needed, how it works: Overloaded method selection only selects methods where the <em>type</em>
- * (not the value!) of the argument is "smaller" or the same as the parameter type. This is similar to how it's in
- * the Java language. That it only decides based on the parameter type is important because this way
- * {@link OverloadedMethodsSubset} can cache method lookup decisions using the types as the cache key. Problem is,
- * since you don't declare the exact numerical types in FTL, and FTL has only a single generic numeric type
- * anyway, what Java type a {@link TemplateNumberModel} uses internally is often seen as a technical detail of which
- * the template author can't always keep track of. So we investigate the <em>value</em> of the number too,
- * then coerce it down without overflow to a type that will match the most overloaded methods. (This
- * is especially important as FTL often stores numbers in {@link BigDecimal}-s, which will hardly ever match any
- * method parameters.) We could simply return that number, like {@code Byte(0)} for an {@code Integer(0)},
- * however, then we would lose the information about what the original type was. The original type is sometimes
- * important, as in ambiguous situations the method where there's an exact type match should be selected (like,
- * when someone wants to select an overload explicitly with {@code m(x?int)}). Also, if an overload wins where
- * the parameter type at the position of the number is {@code Number} or {@code Object} (or {@code Comparable}
- * etc.), it's expected that we pass in the original value (an {@code Integer} in this example), especially if that
- * value is the return value of another Java method. That's why we use
- * {@link NumberWithFallbackType} numerical classes like {@link IntegerOrByte}, which represents both the original
- * type and the coerced type, all encoded into the class of the value, which is used as the overloaded method lookup
- * cache key.
- *
- * <p>See also: <tt>src\main\misc\overloadedNumberRules\prices.ods</tt>.
- *
- * @param num the number to coerce
- * @param typeFlags the type flags of the target parameter position; see {@link TypeFlags}
- *
- * @returns The original number or a {@link NumberWithFallbackType}, depending on the actual value and the types
- * indicated in the {@code targetNumTypes} parameter.
- */
- static Number addFallbackType(final Number num, final int typeFlags) {
- final Class numClass = num.getClass();
- if (numClass == BigDecimal.class) {
- // For now we only support the backward-compatible mode that doesn't prevent roll overs and magnitude loss.
- // However, we push the overloaded selection to the right direction, so we will at least indicate if the
- // number has decimals.
- BigDecimal n = (BigDecimal) num;
- if ((typeFlags & TypeFlags.MASK_KNOWN_INTEGERS) != 0
- && (typeFlags & TypeFlags.MASK_KNOWN_NONINTEGERS) != 0
- && _NumberUtil.isIntegerBigDecimal(n) /* <- can be expensive */) {
- return new IntegerBigDecimal(n);
- } else {
- // Either it was a non-integer, or it didn't mater what it was, as we don't have both integer and
- // non-integer target types.
- return n;
- }
- } else if (numClass == Integer.class) {
- int pn = num.intValue();
- // Note that we try to return the most specific type (i.e., the numerical type with the smallest range), but
- // only among the types that are possible targets. Like if the only target is int and the value is 1, we
- // will return Integer 1, not Byte 1, even though byte is automatically converted to int so it would
- // work too. Why we avoid unnecessarily specific types is that they generate more overloaded method lookup
- // cache entries, since the cache key is the array of the types of the argument values. So we want as few
- // permutations as possible.
- if ((typeFlags & TypeFlags.BYTE) != 0 && pn <= Byte.MAX_VALUE && pn >= Byte.MIN_VALUE) {
- return new IntegerOrByte((Integer) num, (byte) pn);
- } else if ((typeFlags & TypeFlags.SHORT) != 0 && pn <= Short.MAX_VALUE && pn >= Short.MIN_VALUE) {
- return new IntegerOrShort((Integer) num, (short) pn);
- } else {
- return num;
- }
- } else if (numClass == Long.class) {
- final long pn = num.longValue();
- if ((typeFlags & TypeFlags.BYTE) != 0 && pn <= Byte.MAX_VALUE && pn >= Byte.MIN_VALUE) {
- return new LongOrByte((Long) num, (byte) pn);
- } else if ((typeFlags & TypeFlags.SHORT) != 0 && pn <= Short.MAX_VALUE && pn >= Short.MIN_VALUE) {
- return new LongOrShort((Long) num, (short) pn);
- } else if ((typeFlags & TypeFlags.INTEGER) != 0 && pn <= Integer.MAX_VALUE && pn >= Integer.MIN_VALUE) {
- return new LongOrInteger((Long) num, (int) pn);
- } else {
- return num;
- }
- } else if (numClass == Double.class) {
- final double doubleN = num.doubleValue();
-
- // Can we store it in an integer type?
- checkIfWholeNumber: do {
- if ((typeFlags & TypeFlags.MASK_KNOWN_INTEGERS) == 0) break checkIfWholeNumber;
-
- // There's no hope to be 1-precise outside this region. (Although problems can occur even inside it...)
- if (doubleN > MAX_DOUBLE_OR_LONG || doubleN < MIN_DOUBLE_OR_LONG) break checkIfWholeNumber;
-
- long longN = num.longValue();
- double diff = doubleN - longN;
- boolean exact; // We will try to ignore precision glitches (like 0.3 - 0.2 - 0.1 = -2.7E-17)
- if (diff == 0) {
- exact = true;
- } else if (diff > 0) {
- if (diff < LOWEST_ABOVE_ZERO) {
- exact = false;
- } else if (diff > HIGHEST_BELOW_ONE) {
- exact = false;
- longN++;
- } else {
- break checkIfWholeNumber;
- }
- } else { // => diff < 0
- if (diff > -LOWEST_ABOVE_ZERO) {
- exact = false;
- } else if (diff < -HIGHEST_BELOW_ONE) {
- exact = false;
- longN--;
- } else {
- break checkIfWholeNumber;
- }
- }
-
- // If we reach this, it can be treated as a whole number.
-
- if ((typeFlags & TypeFlags.BYTE) != 0
- && longN <= Byte.MAX_VALUE && longN >= Byte.MIN_VALUE) {
- return new DoubleOrByte((Double) num, (byte) longN);
- } else if ((typeFlags & TypeFlags.SHORT) != 0
- && longN <= Short.MAX_VALUE && longN >= Short.MIN_VALUE) {
- return new DoubleOrShort((Double) num, (short) longN);
- } else if ((typeFlags & TypeFlags.INTEGER) != 0
- && longN <= Integer.MAX_VALUE && longN >= Integer.MIN_VALUE) {
- final int intN = (int) longN;
- return (typeFlags & TypeFlags.FLOAT) != 0 && intN >= MIN_FLOAT_OR_INT && intN <= MAX_FLOAT_OR_INT
- ? new DoubleOrIntegerOrFloat((Double) num, intN)
- : new DoubleOrInteger((Double) num, intN);
- } else if ((typeFlags & TypeFlags.LONG) != 0) {
- if (exact) {
- return new DoubleOrLong((Double) num, longN);
- } else {
- // We don't deal with non-exact numbers outside the range of int, as we already reach
- // ULP 2.384185791015625E-7 there.
- if (longN >= Integer.MIN_VALUE && longN <= Integer.MAX_VALUE) {
- return new DoubleOrLong((Double) num, longN);
- } else {
- break checkIfWholeNumber;
- }
- }
- }
- // This point is reached if the double value was out of the range of target integer type(s).
- // Falls through!
- } while (false);
- // If we reach this that means that it can't be treated as a whole number.
-
- if ((typeFlags & TypeFlags.FLOAT) != 0 && doubleN >= -Float.MAX_VALUE && doubleN <= Float.MAX_VALUE) {
- return new DoubleOrFloat((Double) num);
- } else {
- // Simply Double:
- return num;
- }
- } else if (numClass == Float.class) {
- final float floatN = num.floatValue();
-
- // Can we store it in an integer type?
- checkIfWholeNumber: do {
- if ((typeFlags & TypeFlags.MASK_KNOWN_INTEGERS) == 0) break checkIfWholeNumber;
-
- // There's no hope to be 1-precise outside this region. (Although problems can occur even inside it...)
- if (floatN > MAX_FLOAT_OR_INT || floatN < MIN_FLOAT_OR_INT) break checkIfWholeNumber;
-
- int intN = num.intValue();
- double diff = floatN - intN;
- boolean exact; // We will try to ignore precision glitches (like 0.3 - 0.2 - 0.1 = -2.7E-17)
- if (diff == 0) {
- exact = true;
- // We already reach ULP 7.6293945E-6 with bytes, so we don't continue with shorts.
- } else if (intN >= Byte.MIN_VALUE && intN <= Byte.MAX_VALUE) {
- if (diff > 0) {
- if (diff < 0.00001) {
- exact = false;
- } else if (diff > 0.99999) {
- exact = false;
- intN++;
- } else {
- break checkIfWholeNumber;
- }
- } else { // => diff < 0
- if (diff > -0.00001) {
- exact = false;
- } else if (diff < -0.99999) {
- exact = false;
- intN--;
- } else {
- break checkIfWholeNumber;
- }
- }
- } else {
- break checkIfWholeNumber;
- }
-
- // If we reach this, it can be treated as a whole number.
-
- if ((typeFlags & TypeFlags.BYTE) != 0 && intN <= Byte.MAX_VALUE && intN >= Byte.MIN_VALUE) {
- return new FloatOrByte((Float) num, (byte) intN);
- } else if ((typeFlags & TypeFlags.SHORT) != 0 && intN <= Short.MAX_VALUE && intN >= Short.MIN_VALUE) {
- return new FloatOrShort((Float) num, (short) intN);
- } else if ((typeFlags & TypeFlags.INTEGER) != 0) {
- return new FloatOrInteger((Float) num, intN);
- } else if ((typeFlags & TypeFlags.LONG) != 0) {
- // We can't even go outside the range of integers, so we don't need Long variation:
- return exact
- ? new FloatOrInteger((Float) num, intN)
- : new FloatOrByte((Float) num, (byte) intN); // as !exact implies (-128..127)
- }
- // This point is reached if the float value was out of the range of target integer type(s).
- // Falls through!
- } while (false);
- // If we reach this that means that it can't be treated as a whole number. So it's simply a Float:
- return num;
- } else if (numClass == Byte.class) {
- return num;
- } else if (numClass == Short.class) {
- short pn = num.shortValue();
- if ((typeFlags & TypeFlags.BYTE) != 0 && pn <= Byte.MAX_VALUE && pn >= Byte.MIN_VALUE) {
- return new ShortOrByte((Short) num, (byte) pn);
- } else {
- return num;
- }
- } else if (numClass == BigInteger.class) {
- if ((typeFlags
- & ((TypeFlags.MASK_KNOWN_INTEGERS | TypeFlags.MASK_KNOWN_NONINTEGERS)
- ^ (TypeFlags.BIG_INTEGER | TypeFlags.BIG_DECIMAL))) != 0) {
- BigInteger biNum = (BigInteger) num;
- final int bitLength = biNum.bitLength(); // Doesn't include sign bit, so it's one less than expected
- if ((typeFlags & TypeFlags.BYTE) != 0 && bitLength <= 7) {
- return new BigIntegerOrByte(biNum);
- } else if ((typeFlags & TypeFlags.SHORT) != 0 && bitLength <= 15) {
- return new BigIntegerOrShort(biNum);
- } else if ((typeFlags & TypeFlags.INTEGER) != 0 && bitLength <= 31) {
- return new BigIntegerOrInteger(biNum);
- } else if ((typeFlags & TypeFlags.LONG) != 0 && bitLength <= 63) {
- return new BigIntegerOrLong(biNum);
- } else if ((typeFlags & TypeFlags.FLOAT) != 0
- && (bitLength <= MAX_FLOAT_OR_INT_LOG_2
- || bitLength == MAX_FLOAT_OR_INT_LOG_2 + 1
- && biNum.getLowestSetBit() >= MAX_FLOAT_OR_INT_LOG_2)) {
- return new BigIntegerOrFloat(biNum);
- } else if ((typeFlags & TypeFlags.DOUBLE) != 0
- && (bitLength <= MAX_DOUBLE_OR_LONG_LOG_2
- || bitLength == MAX_DOUBLE_OR_LONG_LOG_2 + 1
- && biNum.getLowestSetBit() >= MAX_DOUBLE_OR_LONG_LOG_2)) {
- return new BigIntegerOrDouble(biNum);
- } else {
- return num;
- }
- } else {
- // No relevant coercion target types; return the BigInteger as is:
- return num;
- }
- } else {
- // Unknown number type:
- return num;
- }
- }
-
- interface ByteSource { Byte byteValue(); }
- interface ShortSource { Short shortValue(); }
- interface IntegerSource { Integer integerValue(); }
- interface LongSource { Long longValue(); }
- interface FloatSource { Float floatValue(); }
- interface DoubleSource { Double doubleValue(); }
- interface BigIntegerSource { BigInteger bigIntegerValue(); }
- interface BigDecimalSource { BigDecimal bigDecimalValue(); }
-
- /**
- * Superclass of "Or"-ed numerical types. With an example, a {@code int} 1 has the fallback type {@code byte}, as
- * that's the smallest type that can store the value, so it can be represented as an {@link IntegerOrByte}.
- * This is useful as overloaded method selection only examines the type of the arguments, not the value of them,
- * but with "Or"-ed types we can encode this value-related information into the argument type, hence influencing the
- * method selection.
- */
- abstract static class NumberWithFallbackType extends Number implements Comparable {
-
- protected abstract Number getSourceNumber();
-
- @Override
- public int intValue() {
- return getSourceNumber().intValue();
- }
-
- @Override
- public long longValue() {
- return getSourceNumber().longValue();
- }
-
- @Override
- public float floatValue() {
- return getSourceNumber().floatValue();
- }
-
- @Override
- public double doubleValue() {
- return getSourceNumber().doubleValue();
- }
-
- @Override
- public byte byteValue() {
- return getSourceNumber().byteValue();
- }
-
- @Override
- public short shortValue() {
- return getSourceNumber().shortValue();
- }
-
- @Override
- public int hashCode() {
- return getSourceNumber().hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj != null && getClass() == obj.getClass()) {
- return getSourceNumber().equals(((NumberWithFallbackType) obj).getSourceNumber());
- } else {
- return false;
- }
- }
-
- @Override
- public String toString() {
- return getSourceNumber().toString();
- }
-
- // We have to implement this, so that if a potential matching method expects a Comparable, which is implemented
- // by all the supported numerical types, the "Or" type will be a match.
- @Override
- public int compareTo(Object o) {
- Number n = getSourceNumber();
- if (n instanceof Comparable) {
- return ((Comparable) n).compareTo(o);
- } else {
- throw new ClassCastException(n.getClass().getName() + " is not Comparable.");
- }
- }
-
- }
-
- /**
- * Holds a {@link BigDecimal} that stores a whole number. When selecting a overloaded method, FreeMarker tries to
- * associate {@link BigDecimal} values to parameters of types that can hold non-whole numbers, unless the
- * {@link BigDecimal} is wrapped into this class, in which case it does the opposite. This mechanism is, however,
- * too rough to prevent roll overs or magnitude losses. Those are not yet handled for backward compatibility (they
- * were suppressed earlier too).
- */
- static final class IntegerBigDecimal extends NumberWithFallbackType {
-
- private final BigDecimal n;
-
- IntegerBigDecimal(BigDecimal n) {
- this.n = n;
- }
-
- @Override
- protected Number getSourceNumber() {
- return n;
- }
-
- public BigInteger bigIntegerValue() {
- return n.toBigInteger();
- }
-
- }
-
- static abstract class LongOrSmallerInteger extends NumberWithFallbackType {
-
- private final Long n;
-
- protected LongOrSmallerInteger(Long n) {
- this.n = n;
- }
-
- @Override
- protected Number getSourceNumber() {
- return n;
- }
-
- @Override
- public long longValue() {
- return n.longValue();
- }
-
- }
-
- static class LongOrByte extends LongOrSmallerInteger {
-
- private final byte w;
-
- LongOrByte(Long n, byte w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public byte byteValue() {
- return w;
- }
-
- }
-
- static class LongOrShort extends LongOrSmallerInteger {
-
- private final short w;
-
- LongOrShort(Long n, short w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public short shortValue() {
- return w;
- }
-
- }
-
- static class LongOrInteger extends LongOrSmallerInteger {
-
- private final int w;
-
- LongOrInteger(Long n, int w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public int intValue() {
- return w;
- }
-
- }
-
- static abstract class IntegerOrSmallerInteger extends NumberWithFallbackType {
-
- private final Integer n;
-
- protected IntegerOrSmallerInteger(Integer n) {
- this.n = n;
- }
-
- @Override
- protected Number getSourceNumber() {
- return n;
- }
-
- @Override
- public int intValue() {
- return n.intValue();
- }
-
- }
-
- static class IntegerOrByte extends IntegerOrSmallerInteger {
-
- private final byte w;
-
- IntegerOrByte(Integer n, byte w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public byte byteValue() {
- return w;
- }
-
- }
-
- static class IntegerOrShort extends IntegerOrSmallerInteger {
-
- private final short w;
-
- IntegerOrShort(Integer n, short w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public short shortValue() {
- return w;
- }
-
- }
-
- static class ShortOrByte extends NumberWithFallbackType {
-
- private final Short n;
- private final byte w;
-
- protected ShortOrByte(Short n, byte w) {
- this.n = n;
- this.w = w;
- }
-
- @Override
- protected Number getSourceNumber() {
- return n;
- }
-
- @Override
- public short shortValue() {
- return n.shortValue();
- }
-
- @Override
- public byte byteValue() {
- return w;
- }
-
- }
-
- static abstract class DoubleOrWholeNumber extends NumberWithFallbackType {
-
- private final Double n;
-
- protected DoubleOrWholeNumber(Double n) {
- this.n = n;
- }
-
- @Override
- protected Number getSourceNumber() {
- return n;
- }
-
- @Override
- public double doubleValue() {
- return n.doubleValue();
- }
-
- }
-
- static final class DoubleOrByte extends DoubleOrWholeNumber {
-
- private final byte w;
-
- DoubleOrByte(Double n, byte w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public byte byteValue() {
- return w;
- }
-
- @Override
- public short shortValue() {
- return w;
- }
-
- @Override
- public int intValue() {
- return w;
- }
-
- @Override
- public long longValue() {
- return w;
- }
-
- }
-
- static final class DoubleOrShort extends DoubleOrWholeNumber {
-
- private final short w;
-
- DoubleOrShort(Double n, short w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public short shortValue() {
- return w;
- }
-
- @Override
- public int intValue() {
- return w;
- }
-
- @Override
- public long longValue() {
- return w;
- }
-
- }
-
- static final class DoubleOrIntegerOrFloat extends DoubleOrWholeNumber {
-
- private final int w;
-
- DoubleOrIntegerOrFloat(Double n, int w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public int intValue() {
- return w;
- }
-
- @Override
- public long longValue() {
- return w;
- }
-
- }
-
- static final class DoubleOrInteger extends DoubleOrWholeNumber {
-
- private final int w;
-
- DoubleOrInteger(Double n, int w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public int intValue() {
- return w;
- }
-
- @Override
- public long longValue() {
- return w;
- }
-
- }
-
- static final class DoubleOrLong extends DoubleOrWholeNumber {
-
- private final long w;
-
- DoubleOrLong(Double n, long w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public long longValue() {
- return w;
- }
-
- }
-
- static final class DoubleOrFloat extends NumberWithFallbackType {
-
- private final Double n;
-
- DoubleOrFloat(Double n) {
- this.n = n;
- }
-
- @Override
- public float floatValue() {
- return n.floatValue();
- }
-
- @Override
- public double doubleValue() {
- return n.doubleValue();
- }
-
- @Override
- protected Number getSourceNumber() {
- return n;
- }
-
- }
-
- static abstract class FloatOrWholeNumber extends NumberWithFallbackType {
-
- private final Float n;
-
- FloatOrWholeNumber(Float n) {
- this.n = n;
- }
-
- @Override
- protected Number getSourceNumber() {
- return n;
- }
-
- @Override
- public float floatValue() {
- return n.floatValue();
- }
-
- }
-
- static final class FloatOrByte extends FloatOrWholeNumber {
-
- private final byte w;
-
- FloatOrByte(Float n, byte w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public byte byteValue() {
- return w;
- }
-
- @Override
- public short shortValue() {
- return w;
- }
-
- @Override
- public int intValue() {
- return w;
- }
-
- @Override
- public long longValue() {
- return w;
- }
-
- }
-
- static final class FloatOrShort extends FloatOrWholeNumber {
-
- private final short w;
-
- FloatOrShort(Float n, short w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public short shortValue() {
- return w;
- }
-
- @Override
- public int intValue() {
- return w;
- }
-
- @Override
- public long longValue() {
- return w;
- }
-
- }
-
- static final class FloatOrInteger extends FloatOrWholeNumber {
-
- private final int w;
-
- FloatOrInteger(Float n, int w) {
- super(n);
- this.w = w;
- }
-
- @Override
- public int intValue() {
- return w;
- }
-
- @Override
- public long longValue() {
- return w;
- }
-
- }
-
- abstract static class BigIntegerOrPrimitive extends NumberWithFallbackType {
-
- protected final BigInteger n;
-
- BigIntegerOrPrimitive(BigInteger n) {
- this.n = n;
- }
-
- @Override
- protected Number getSourceNumber() {
- return n;
- }
-
- }
-
- final static class BigIntegerOrByte extends BigIntegerOrPrimitive {
-
- BigIntegerOrByte(BigInteger n) {
- super(n);
- }
-
- }
-
- final static class BigIntegerOrShort extends BigIntegerOrPrimitive {
-
- BigIntegerOrShort(BigInteger n) {
- super(n);
- }
-
- }
-
- final static class BigIntegerOrInteger extends BigIntegerOrPrimitive {
-
- BigIntegerOrInteger(BigInteger n) {
- super(n);
- }
-
- }
-
- final static class BigIntegerOrLong extends BigIntegerOrPrimitive {
-
- BigIntegerOrLong(BigInteger n) {
- super(n);
- }
-
- }
-
- abstract static class BigIntegerOrFPPrimitive extends BigIntegerOrPrimitive {
-
- BigIntegerOrFPPrimitive(BigInteger n) {
- super(n);
- }
-
- /** Faster version of {@link BigDecimal#floatValue()}, utilizes that the number known to fit into a long. */
- @Override
- public float floatValue() {
- return n.longValue();
- }
-
- /** Faster version of {@link BigDecimal#doubleValue()}, utilizes that the number known to fit into a long. */
- @Override
- public double doubleValue() {
- return n.longValue();
- }
-
- }
-
- final static class BigIntegerOrFloat extends BigIntegerOrFPPrimitive {
-
- BigIntegerOrFloat(BigInteger n) {
- super(n);
- }
-
- }
-
- final static class BigIntegerOrDouble extends BigIntegerOrFPPrimitive {
-
- BigIntegerOrDouble(BigInteger n) {
- super(n);
- }
-
- }
-
- /**
- * Returns a non-negative number that indicates how much we want to avoid a given numerical type conversion. Since
- * we only consider the types here, not the actual value, we always consider the worst case scenario. Like it will
- * say that converting int to short is not allowed, although int 1 can be converted to byte without loss. To account
- * for such situations, "Or"-ed types, like {@link IntegerOrByte} has to be used.
- *
- * @param fromC the non-primitive type of the argument (with other words, the actual type).
- * Must be {@link Number} or its subclass. This is possibly an {@link NumberWithFallbackType} subclass.
- * @param toC the <em>non-primitive</em> type of the target parameter (with other words, the format type).
- * Must be a {@link Number} subclass, not {@link Number} itself.
- * Must <em>not</em> be {@link NumberWithFallbackType} or its subclass.
- *
- * @return
- * <p>The possible values are:
- * <ul>
- * <li>0: No conversion is needed
- * <li>[0, 30000): Lossless conversion
- * <li>[30000, 40000): Smaller precision loss in mantissa is possible.
- * <li>[40000, 50000): Bigger precision loss in mantissa is possible.
- * <li>{@link Integer#MAX_VALUE}: Conversion not allowed due to the possibility of magnitude loss or
- * overflow</li>
- * </ul>
- *
- * <p>At some places, we only care if the conversion is possible, i.e., whether the return value is
- * {@link Integer#MAX_VALUE} or not. But when multiple overloaded methods have an argument type to which we
- * could convert to, this number will influence which of those will be chosen.
- */
- static int getArgumentConversionPrice(Class fromC, Class toC) {
- // DO NOT EDIT, generated code!
- // See: src\main\misc\overloadedNumberRules\README.txt
- if (toC == fromC) {
- return 0;
- } else if (toC == Integer.class) {
- if (fromC == IntegerBigDecimal.class) return 31003;
- else if (fromC == BigDecimal.class) return 41003;
- else if (fromC == Long.class) return Integer.MAX_VALUE;
- else if (fromC == Double.class) return Integer.MAX_VALUE;
- else if (fromC == Float.class) return Integer.MAX_VALUE;
- else if (fromC == Byte.class) return 10003;
- else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
- else if (fromC == LongOrInteger.class) return 21003;
- else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrIntegerOrFloat.class) return 22003;
- else if (fromC == DoubleOrInteger.class) return 22003;
- else if (fromC == DoubleOrLong.class) return Integer.MAX_VALUE;
- else if (fromC == IntegerOrByte.class) return 0;
- else if (fromC == DoubleOrByte.class) return 22003;
- else if (fromC == LongOrByte.class) return 21003;
- else if (fromC == Short.class) return 10003;
- else if (fromC == LongOrShort.class) return 21003;
- else if (fromC == ShortOrByte.class) return 10003;
- else if (fromC == FloatOrInteger.class) return 21003;
- else if (fromC == FloatOrByte.class) return 21003;
- else if (fromC == FloatOrShort.class) return 21003;
- else if (fromC == BigIntegerOrInteger.class) return 16003;
- else if (fromC == BigIntegerOrLong.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrByte.class) return 16003;
- else if (fromC == IntegerOrShort.class) return 0;
- else if (fromC == DoubleOrShort.class) return 22003;
- else if (fromC == BigIntegerOrShort.class) return 16003;
- else return Integer.MAX_VALUE;
- } else if (toC == Long.class) {
- if (fromC == Integer.class) return 10004;
- else if (fromC == IntegerBigDecimal.class) return 31004;
- else if (fromC == BigDecimal.class) return 41004;
- else if (fromC == Double.class) return Integer.MAX_VALUE;
- else if (fromC == Float.class) return Integer.MAX_VALUE;
- else if (fromC == Byte.class) return 10004;
- else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
- else if (fromC == LongOrInteger.class) return 0;
- else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrIntegerOrFloat.class) return 21004;
- else if (fromC == DoubleOrInteger.class) return 21004;
- else if (fromC == DoubleOrLong.class) return 21004;
- else if (fromC == IntegerOrByte.class) return 10004;
- else if (fromC == DoubleOrByte.class) return 21004;
- else if (fromC == LongOrByte.class) return 0;
- else if (fromC == Short.class) return 10004;
- else if (fromC == LongOrShort.class) return 0;
- else if (fromC == ShortOrByte.class) return 10004;
- else if (fromC == FloatOrInteger.class) return 21004;
- else if (fromC == FloatOrByte.class) return 21004;
- else if (fromC == FloatOrShort.class) return 21004;
- else if (fromC == BigIntegerOrInteger.class) return 15004;
- else if (fromC == BigIntegerOrLong.class) return 15004;
- else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrByte.class) return 15004;
- else if (fromC == IntegerOrShort.class) return 10004;
- else if (fromC == DoubleOrShort.class) return 21004;
- else if (fromC == BigIntegerOrShort.class) return 15004;
- else return Integer.MAX_VALUE;
- } else if (toC == Double.class) {
- if (fromC == Integer.class) return 20007;
- else if (fromC == IntegerBigDecimal.class) return 32007;
- else if (fromC == BigDecimal.class) return 32007;
- else if (fromC == Long.class) return 30007;
- else if (fromC == Float.class) return 10007;
- else if (fromC == Byte.class) return 20007;
- else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
- else if (fromC == LongOrInteger.class) return 21007;
- else if (fromC == DoubleOrFloat.class) return 0;
- else if (fromC == DoubleOrIntegerOrFloat.class) return 0;
- else if (fromC == DoubleOrInteger.class) return 0;
- else if (fromC == DoubleOrLong.class) return 0;
- else if (fromC == IntegerOrByte.class) return 20007;
- else if (fromC == DoubleOrByte.class) return 0;
- else if (fromC == LongOrByte.class) return 21007;
- else if (fromC == Short.class) return 20007;
- else if (fromC == LongOrShort.class) return 21007;
- else if (fromC == ShortOrByte.class) return 20007;
- else if (fromC == FloatOrInteger.class) return 10007;
- else if (fromC == FloatOrByte.class) return 10007;
- else if (fromC == FloatOrShort.class) return 10007;
- else if (fromC == BigIntegerOrInteger.class) return 20007;
- else if (fromC == BigIntegerOrLong.class) return 30007;
- else if (fromC == BigIntegerOrDouble.class) return 20007;
- else if (fromC == BigIntegerOrFloat.class) return 20007;
- else if (fromC == BigIntegerOrByte.class) return 20007;
- else if (fromC == IntegerOrShort.class) return 20007;
- else if (fromC == DoubleOrShort.class) return 0;
- else if (fromC == BigIntegerOrShort.class) return 20007;
- else return Integer.MAX_VALUE;
- } else if (toC == Float.class) {
- if (fromC == Integer.class) return 30006;
- else if (fromC == IntegerBigDecimal.class) return 33006;
- else if (fromC == BigDecimal.class) return 33006;
- else if (fromC == Long.class) return 40006;
- else if (fromC == Double.class) return Integer.MAX_VALUE;
- else if (fromC == Byte.class) return 20006;
- else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
- else if (fromC == LongOrInteger.class) return 30006;
- else if (fromC == DoubleOrFloat.class) return 30006;
- else if (fromC == DoubleOrIntegerOrFloat.class) return 23006;
- else if (fromC == DoubleOrInteger.class) return 30006;
- else if (fromC == DoubleOrLong.class) return 40006;
- else if (fromC == IntegerOrByte.class) return 24006;
- else if (fromC == DoubleOrByte.class) return 23006;
- else if (fromC == LongOrByte.class) return 24006;
- else if (fromC == Short.class) return 20006;
- else if (fromC == LongOrShort.class) return 24006;
- else if (fromC == ShortOrByte.class) return 20006;
- else if (fromC == FloatOrInteger.class) return 0;
- else if (fromC == FloatOrByte.class) return 0;
- else if (fromC == FloatOrShort.class) return 0;
- else if (fromC == BigIntegerOrInteger.class) return 30006;
- else if (fromC == BigIntegerOrLong.class) return 40006;
- else if (fromC == BigIntegerOrDouble.class) return 40006;
- else if (fromC == BigIntegerOrFloat.class) return 24006;
- else if (fromC == BigIntegerOrByte.class) return 24006;
- else if (fromC == IntegerOrShort.class) return 24006;
- else if (fromC == DoubleOrShort.class) return 23006;
- else if (fromC == BigIntegerOrShort.class) return 24006;
- else return Integer.MAX_VALUE;
- } else if (toC == Byte.class) {
- if (fromC == Integer.class) return Integer.MAX_VALUE;
- else if (fromC == IntegerBigDecimal.class) return 35001;
- else if (fromC == BigDecimal.class) return 45001;
- else if (fromC == Long.class) return Integer.MAX_VALUE;
- else if (fromC == Double.class) return Integer.MAX_VALUE;
- else if (fromC == Float.class) return Integer.MAX_VALUE;
- else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
- else if (fromC == LongOrInteger.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrIntegerOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrInteger.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrLong.class) return Integer.MAX_VALUE;
- else if (fromC == IntegerOrByte.class) return 22001;
- else if (fromC == DoubleOrByte.class) return 25001;
- else if (fromC == LongOrByte.class) return 23001;
- else if (fromC == Short.class) return Integer.MAX_VALUE;
- else if (fromC == LongOrShort.class) return Integer.MAX_VALUE;
- else if (fromC == ShortOrByte.class) return 21001;
- else if (fromC == FloatOrInteger.class) return Integer.MAX_VALUE;
- else if (fromC == FloatOrByte.class) return 23001;
- else if (fromC == FloatOrShort.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrInteger.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrLong.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrByte.class) return 18001;
- else if (fromC == IntegerOrShort.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrShort.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrShort.class) return Integer.MAX_VALUE;
- else return Integer.MAX_VALUE;
- } else if (toC == Short.class) {
- if (fromC == Integer.class) return Integer.MAX_VALUE;
- else if (fromC == IntegerBigDecimal.class) return 34002;
- else if (fromC == BigDecimal.class) return 44002;
- else if (fromC == Long.class) return Integer.MAX_VALUE;
- else if (fromC == Double.class) return Integer.MAX_VALUE;
- else if (fromC == Float.class) return Integer.MAX_VALUE;
- else if (fromC == Byte.class) return 10002;
- else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
- else if (fromC == LongOrInteger.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrIntegerOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrInteger.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrLong.class) return Integer.MAX_VALUE;
- else if (fromC == IntegerOrByte.class) return 21002;
- else if (fromC == DoubleOrByte.class) return 24002;
- else if (fromC == LongOrByte.class) return 22002;
- else if (fromC == LongOrShort.class) return 22002;
- else if (fromC == ShortOrByte.class) return 0;
- else if (fromC == FloatOrInteger.class) return Integer.MAX_VALUE;
- else if (fromC == FloatOrByte.class) return 22002;
- else if (fromC == FloatOrShort.class) return 22002;
- else if (fromC == BigIntegerOrInteger.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrLong.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == BigIntegerOrByte.class) return 17002;
- else if (fromC == IntegerOrShort.class) return 21002;
- else if (fromC == DoubleOrShort.class) return 24002;
- else if (fromC == BigIntegerOrShort.class) return 17002;
- else return Integer.MAX_VALUE;
- } else if (toC == BigDecimal.class) {
- if (fromC == Integer.class) return 20008;
- else if (fromC == IntegerBigDecimal.class) return 0;
- else if (fromC == Long.class) return 20008;
- else if (fromC == Double.class) return 20008;
- else if (fromC == Float.class) return 20008;
- else if (fromC == Byte.class) return 20008;
- else if (fromC == BigInteger.class) return 10008;
- else if (fromC == LongOrInteger.class) return 20008;
- else if (fromC == DoubleOrFloat.class) return 20008;
- else if (fromC == DoubleOrIntegerOrFloat.class) return 20008;
- else if (fromC == DoubleOrInteger.class) return 20008;
- else if (fromC == DoubleOrLong.class) return 20008;
- else if (fromC == IntegerOrByte.class) return 20008;
- else if (fromC == DoubleOrByte.class) return 20008;
- else if (fromC == LongOrByte.class) return 20008;
- else if (fromC == Short.class) return 20008;
- else if (fromC == LongOrShort.class) return 20008;
- else if (fromC == ShortOrByte.class) return 20008;
- else if (fromC == FloatOrInteger.class) return 20008;
- else if (fromC == FloatOrByte.class) return 20008;
- else if (fromC == FloatOrShort.class) return 20008;
- else if (fromC == BigIntegerOrInteger.class) return 10008;
- else if (fromC == BigIntegerOrLong.class) return 10008;
- else if (fromC == BigIntegerOrDouble.class) return 10008;
- else if (fromC == BigIntegerOrFloat.class) return 10008;
- else if (fromC == BigIntegerOrByte.class) return 10008;
- else if (fromC == IntegerOrShort.class) return 20008;
- else if (fromC == DoubleOrShort.class) return 20008;
- else if (fromC == BigIntegerOrShort.class) return 10008;
- else return Integer.MAX_VALUE;
- } else if (toC == BigInteger.class) {
- if (fromC == Integer.class) return 10005;
- else if (fromC == IntegerBigDecimal.class) return 10005;
- else if (fromC == BigDecimal.class) return 40005;
- else if (fromC == Long.class) return 10005;
- else if (fromC == Double.class) return Integer.MAX_VALUE;
- else if (fromC == Float.class) return Integer.MAX_VALUE;
- else if (fromC == Byte.class) return 10005;
- else if (fromC == LongOrInteger.class) return 10005;
- else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
- else if (fromC == DoubleOrIntegerOrFloat.class) return 21005;
- else if (fromC == DoubleOrInteger.class) return 21005;
- else if (fromC == DoubleOrLong.class) return 21005;
- else if (fromC == IntegerOrByte.class) return 10005;
- else if (fromC == DoubleOrByte.class) return 21005;
- else if (fromC == LongOrByte.class) return 10005;
- else if (fromC == Short.class) return 10005;
- else if (fromC == LongOrShort.class) return 10005;
- else if (fromC == ShortOrByte.class) return 10005;
- else if (fromC == FloatOrInteger.class) return 25005;
- else if (fromC == FloatOrByte.class) return 25005;
- else if (fromC == FloatOrShort.class) return 25005;
- else if (fromC == BigIntegerOrInteger.class) return 0;
- else if (fromC == BigIntegerOrLong.class) return 0;
- else if (fromC == BigIntegerOrDouble.class) return 0;
- else if (fromC == BigIntegerOrFloat.class) return 0;
- else if (fromC == BigIntegerOrByte.class) return 0;
- else if (fromC == IntegerOrShort.class) return 10005;
- else if (fromC == DoubleOrShort.class) return 21005;
- else if (fromC == BigIntegerOrShort.class) return 0;
- else return Integer.MAX_VALUE;
- } else {
- // Unknown toC; we don't know how to convert to it:
- return Integer.MAX_VALUE;
- }
- }
-
- static int compareNumberTypeSpecificity(Class c1, Class c2) {
- // DO NOT EDIT, generated code!
- // See: src\main\misc\overloadedNumberRules\README.txt
- c1 = _ClassUtil.primitiveClassToBoxingClass(c1);
- c2 = _ClassUtil.primitiveClassToBoxingClass(c2);
-
- if (c1 == c2) return 0;
-
- if (c1 == Integer.class) {
- if (c2 == Long.class) return 4 - 3;
- if (c2 == Double.class) return 7 - 3;
- if (c2 == Float.class) return 6 - 3;
- if (c2 == Byte.class) return 1 - 3;
- if (c2 == Short.class) return 2 - 3;
- if (c2 == BigDecimal.class) return 8 - 3;
- if (c2 == BigInteger.class) return 5 - 3;
- return 0;
- }
- if (c1 == Long.class) {
- if (c2 == Integer.class) return 3 - 4;
- if (c2 == Double.class) return 7 - 4;
- if (c2 == Float.class) return 6 - 4;
- if (c2 == Byte.class) return 1 - 4;
- if (c2 == Short.class) return 2 - 4;
- if (c2 == BigDecimal.class) return 8 - 4;
- if (c2 == BigInteger.class) return 5 - 4;
- return 0;
- }
- if (c1 == Double.class) {
- if (c2 == Integer.class) return 3 - 7;
- if (c2 == Long.class) return 4 - 7;
- if (c2 == Float.class) return 6 - 7;
- if (c2 == Byte.class) return 1 - 7;
- if (c2 == Short.class) return 2 - 7;
- if (c2 == BigDecimal.class) return 8 - 7;
- if (c2 == BigInteger.class) return 5 - 7;
- return 0;
- }
- if (c1 == Float.class) {
- if (c2 == Integer.class) return 3 - 6;
- if (c2 == Long.class) return 4 - 6;
- if (c2 == Double.class) return 7 - 6;
- if (c2 == Byte.class) return 1 - 6;
- if (c2 == Short.class) return 2 - 6;
- if (c2 == BigDecimal.class) return 8 - 6;
- if (c2 == BigInteger.class) return 5 - 6;
- return 0;
- }
- if (c1 == Byte.class) {
- if (c2 == Integer.class) return 3 - 1;
- if (c2 == Long.class) return 4 - 1;
- if (c2 == Double.class) return 7 - 1;
- if (c2 == Float.class) return 6 - 1;
- if (c2 == Short.class) return 2 - 1;
- if (c2 == BigDecimal.class) return 8 - 1;
- if (c2 == BigInteger.class) return 5 - 1;
- return 0;
- }
- if (c1 == Short.class) {
- if (c2 == Integer.class) return 3 - 2;
- if (c2 == Long.class) return 4 - 2;
- if (c2 == Double.class) return 7 - 2;
- if (c2 == Float.class) return 6 - 2;
- if (c2 == Byte.class) return 1 - 2;
- if (c2 == BigDecimal.class) return 8 - 2;
- if (c2 == BigInteger.class) return 5 - 2;
- return 0;
- }
- if (c1 == BigDecimal.class) {
- if (c2 == Integer.class) return 3 - 8;
- if (c2 == Long.class) return 4 - 8;
- if (c2 == Double.class) return 7 - 8;
- if (c2 == Float.class) return 6 - 8;
- if (c2 == Byte.class) return 1 - 8;
- if (c2 == Short.class) return 2 - 8;
- if (c2 == BigInteger.class) return 5 - 8;
- return 0;
- }
- if (c1 == BigInteger.class) {
- if (c2 == Integer.class) return 3 - 5;
- if (c2 == Long.class) return 4 - 5;
- if (c2 == Double.class) return 7 - 5;
- if (c2 == Float.class) return 6 - 5;
- if (c2 == Byte.class) return 1 - 5;
- if (c2 == Short.class) return 2 - 5;
- if (c2 == BigDecimal.class) return 8 - 5;
- return 0;
- }
- return 0;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedVarArgsMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedVarArgsMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedVarArgsMethods.java
deleted file mode 100644
index 4a6d277..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedVarArgsMethods.java
+++ /dev/null
@@ -1,245 +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.Array;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-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.util.BugException;
-
-/**
- * Stores the varargs methods for a {@link OverloadedMethods} object.
- */
-class OverloadedVarArgsMethods extends OverloadedMethodsSubset {
-
- OverloadedVarArgsMethods() {
- super();
- }
-
- /**
- * Replaces the last parameter type with the array component type of it.
- */
- @Override
- Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) {
- final Class[] preprocessedParamTypes = memberDesc.getParamTypes().clone();
- int ln = preprocessedParamTypes.length;
- final Class varArgsCompType = preprocessedParamTypes[ln - 1].getComponentType();
- if (varArgsCompType == null) {
- throw new BugException("Only varargs methods should be handled here");
- }
- preprocessedParamTypes[ln - 1] = varArgsCompType;
- return preprocessedParamTypes;
- }
-
- @Override
- void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes) {
- // Overview
- // --------
- //
- // So far, m(t1, t2...) was treated by the hint widening like m(t1, t2). So now we have to continue hint
- // widening like if we had further methods:
- // - m(t1, t2, t2), m(t1, t2, t2, t2), ...
- // - m(t1), because a varargs array can be 0 long
- //
- // But we can't do that for real, because we had to add infinite number of methods. Also, for efficiency we
- // don't want to create unwrappingHintsByParamCount entries at the indices which are still unused.
- // So we only update the already existing hints. Remember that we already have m(t1, t2) there.
-
- final int paramCount = paramTypes.length;
- final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
-
- // The case of e(t1, t2), e(t1, t2, t2), e(t1, t2, t2, t2), ..., where e is an *earlierly* added method.
- // When that was added, this method wasn't added yet, so it had no chance updating the hints of this method,
- // so we do that now:
- // FIXME: Only needed if m(t1, t2) was filled an empty slot, otherwise whatever was there was already
- // widened by the preceding hints, so this will be a no-op.
- for (int i = paramCount - 1; i >= 0; i--) {
- final Class[] previousHints = unwrappingHintsByParamCount[i];
- if (previousHints != null) {
- widenHintsToCommonSupertypes(
- paramCount,
- previousHints, getTypeFlags(i));
- break; // we only do this for the first hit, as the methods before that has already widened it.
- }
- }
- // The case of e(t1), where e is an *earlier* added method.
- // When that was added, this method wasn't added yet, so it had no chance updating the hints of this method,
- // so we do that now:
- // FIXME: Same as above; it's often unnecessary.
- if (paramCount + 1 < unwrappingHintsByParamCount.length) {
- Class[] oneLongerHints = unwrappingHintsByParamCount[paramCount + 1];
- if (oneLongerHints != null) {
- widenHintsToCommonSupertypes(
- paramCount,
- oneLongerHints, getTypeFlags(paramCount + 1));
- }
- }
-
- // The case of m(t1, t2, t2), m(t1, t2, t2, t2), ..., where m is the currently added method.
- // Update the longer hints-arrays:
- for (int i = paramCount + 1; i < unwrappingHintsByParamCount.length; i++) {
- widenHintsToCommonSupertypes(
- i,
- paramTypes, paramNumericalTypes);
- }
- // The case of m(t1) where m is the currently added method.
- // update the one-shorter hints-array:
- if (paramCount > 0) { // (should be always true, or else it wasn't a varags method)
- widenHintsToCommonSupertypes(
- paramCount - 1,
- paramTypes, paramNumericalTypes);
- }
-
- }
-
- private void widenHintsToCommonSupertypes(
- int paramCountOfWidened, Class[] wideningTypes, int[] wideningTypeFlags) {
- final Class[] typesToWiden = getUnwrappingHintsByParamCount()[paramCountOfWidened];
- if (typesToWiden == null) {
- return; // no such overload exists; nothing to widen
- }
-
- final int typesToWidenLen = typesToWiden.length;
- final int wideningTypesLen = wideningTypes.length;
- int min = Math.min(wideningTypesLen, typesToWidenLen);
- for (int i = 0; i < min; ++i) {
- typesToWiden[i] = getCommonSupertypeForUnwrappingHint(typesToWiden[i], wideningTypes[i]);
- }
- if (typesToWidenLen > wideningTypesLen) {
- Class varargsComponentType = wideningTypes[wideningTypesLen - 1];
- for (int i = wideningTypesLen; i < typesToWidenLen; ++i) {
- typesToWiden[i] = getCommonSupertypeForUnwrappingHint(typesToWiden[i], varargsComponentType);
- }
- }
-
- mergeInTypesFlags(paramCountOfWidened, wideningTypeFlags);
- }
-
- @Override
- MaybeEmptyMemberAndArguments getMemberAndArguments(List tmArgs, BeansWrapper unwrapper)
- throws TemplateModelException {
- if (tmArgs == null) {
- // null is treated as empty args
- tmArgs = Collections.EMPTY_LIST;
- }
- final int argsLen = tmArgs.size();
- final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
- final Object[] pojoArgs = new Object[argsLen];
- int[] typesFlags = null;
- // Going down starting from methods with args.length + 1 parameters, because we must try to match against a case
- // where all specified args are fixargs, and we have 0 varargs.
- outer: for (int paramCount = Math.min(argsLen + 1, unwrappingHintsByParamCount.length - 1); paramCount >= 0; --paramCount) {
- Class[] unwarappingHints = unwrappingHintsByParamCount[paramCount];
- if (unwarappingHints == null) {
- if (paramCount == 0) {
- return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS;
- }
- continue;
- }
-
- typesFlags = getTypeFlags(paramCount);
- if (typesFlags == ALL_ZEROS_ARRAY) {
- typesFlags = null;
- }
-
- // Try to unwrap the arguments
- Iterator it = tmArgs.iterator();
- for (int i = 0; i < argsLen; ++i) {
- int paramIdx = i < paramCount ? i : paramCount - 1;
- Object pojo = unwrapper.tryUnwrapTo(
- (TemplateModel) it.next(),
- unwarappingHints[paramIdx],
- typesFlags != null ? typesFlags[paramIdx] : 0);
- if (pojo == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
- continue outer;
- }
- pojoArgs[i] = pojo;
- }
- break outer;
- }
-
- MaybeEmptyCallableMemberDescriptor maybeEmtpyMemberDesc = getMemberDescriptorForArgs(pojoArgs, true);
- if (maybeEmtpyMemberDesc instanceof CallableMemberDescriptor) {
- CallableMemberDescriptor memberDesc = (CallableMemberDescriptor) maybeEmtpyMemberDesc;
- Object[] pojoArgsWithArray;
- Object argsOrErrorIdx = replaceVarargsSectionWithArray(pojoArgs, tmArgs, memberDesc, unwrapper);
- if (argsOrErrorIdx instanceof Object[]) {
- pojoArgsWithArray = (Object[]) argsOrErrorIdx;
- } else {
- return EmptyMemberAndArguments.noCompatibleOverload(((Integer) argsOrErrorIdx).intValue());
- }
- if (typesFlags != null) {
- // Note that overloaded method selection has already accounted for overflow errors when the method
- // was selected. So this forced conversion shouldn't cause such corruption. Except, conversion from
- // BigDecimal is allowed to overflow for backward-compatibility.
- forceNumberArgumentsToParameterTypes(pojoArgsWithArray, memberDesc.getParamTypes(), typesFlags);
- }
- return new MemberAndArguments(memberDesc, pojoArgsWithArray);
- } else {
- return EmptyMemberAndArguments.from((EmptyCallableMemberDescriptor) maybeEmtpyMemberDesc, pojoArgs);
- }
- }
-
- /**
- * Converts a flat argument list to one where the last argument is an array that collects the varargs, also
- * re-unwraps the varargs to the component type. Note that this couldn't be done until we had the concrete
- * member selected.
- *
- * @return An {@code Object[]} if everything went well, or an {@code Integer} the
- * order (1-based index) of the argument that couldn't be unwrapped.
- */
- private Object replaceVarargsSectionWithArray(
- Object[] args, List modelArgs, CallableMemberDescriptor memberDesc, BeansWrapper unwrapper)
- throws TemplateModelException {
- final Class[] paramTypes = memberDesc.getParamTypes();
- final int paramCount = paramTypes.length;
- final Class varArgsCompType = paramTypes[paramCount - 1].getComponentType();
- final int totalArgCount = args.length;
- final int fixArgCount = paramCount - 1;
- if (args.length != paramCount) {
- Object[] packedArgs = new Object[paramCount];
- System.arraycopy(args, 0, packedArgs, 0, fixArgCount);
- Object varargs = Array.newInstance(varArgsCompType, totalArgCount - fixArgCount);
- for (int i = fixArgCount; i < totalArgCount; ++i) {
- Object val = unwrapper.tryUnwrapTo((TemplateModel) modelArgs.get(i), varArgsCompType);
- if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
- return Integer.valueOf(i + 1);
- }
- Array.set(varargs, i - fixArgCount, val);
- }
- packedArgs[fixArgCount] = varargs;
- return packedArgs;
- } else {
- Object val = unwrapper.tryUnwrapTo((TemplateModel) modelArgs.get(fixArgCount), varArgsCompType);
- if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
- return Integer.valueOf(fixArgCount + 1);
- }
- Object array = Array.newInstance(varArgsCompType, 1);
- Array.set(array, 0, val);
- args[fixArgCount] = array;
- return args;
- }
- }
-
-}
\ No newline at end of file
[14/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java b/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
new file mode 100644
index 0000000..633687d
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java
@@ -0,0 +1,293 @@
+/*
+ * 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.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/_ModelAPI.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.java b/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.java
new file mode 100644
index 0000000..660173a
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.java
@@ -0,0 +1,220 @@
+/*
+ * 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.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util.BugException;
+import org.apache.freemarker.core.util._CollectionUtil;
+
+/**
+ * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+ * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can
+ * access things inside this package that users shouldn't.
+ */
+public class _ModelAPI {
+
+ private _ModelAPI() { }
+
+ public static Object newInstance(Class<?> pClass, Object[] args, DefaultObjectWrapper ow)
+ throws NoSuchMethodException, IllegalArgumentException, InstantiationException,
+ IllegalAccessException, InvocationTargetException, TemplateModelException {
+ return newInstance(getConstructorDescriptor(pClass, args), args, ow);
+ }
+
+ /**
+ * Gets the constructor that matches the types of the arguments the best. So this is more
+ * than what the Java reflection API provides in that it can handle overloaded constructors. This re-uses the
+ * overloaded method selection logic of {@link DefaultObjectWrapper}.
+ */
+ private static CallableMemberDescriptor getConstructorDescriptor(Class<?> pClass, Object[] args)
+ throws NoSuchMethodException {
+ if (args == null) args = _CollectionUtil.EMPTY_OBJECT_ARRAY;
+
+ final ArgumentTypes argTypes = new ArgumentTypes(args);
+ final List<ReflectionCallableMemberDescriptor> fixedArgMemberDescs
+ = new ArrayList<>();
+ final List<ReflectionCallableMemberDescriptor> varArgsMemberDescs
+ = new ArrayList<>();
+ for (Constructor<?> constr : pClass.getConstructors()) {
+ ReflectionCallableMemberDescriptor memberDesc = new ReflectionCallableMemberDescriptor(constr, constr.getParameterTypes());
+ if (!_MethodUtil.isVarargs(constr)) {
+ fixedArgMemberDescs.add(memberDesc);
+ } else {
+ varArgsMemberDescs.add(memberDesc);
+ }
+ }
+
+ MaybeEmptyCallableMemberDescriptor contrDesc = argTypes.getMostSpecific(fixedArgMemberDescs, false);
+ if (contrDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) {
+ contrDesc = argTypes.getMostSpecific(varArgsMemberDescs, true);
+ }
+
+ if (contrDesc instanceof EmptyCallableMemberDescriptor) {
+ if (contrDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) {
+ throw new NoSuchMethodException(
+ "There's no public " + pClass.getName()
+ + " constructor with compatible parameter list.");
+ } else if (contrDesc == EmptyCallableMemberDescriptor.AMBIGUOUS_METHOD) {
+ throw new NoSuchMethodException(
+ "There are multiple public " + pClass.getName()
+ + " constructors that match the compatible parameter list with the same preferability.");
+ } else {
+ throw new NoSuchMethodException();
+ }
+ } else {
+ return (CallableMemberDescriptor) contrDesc;
+ }
+ }
+
+ private static Object newInstance(CallableMemberDescriptor constrDesc, Object[] args, DefaultObjectWrapper ow)
+ throws InstantiationException, IllegalAccessException, InvocationTargetException, IllegalArgumentException,
+ TemplateModelException {
+ if (args == null) args = _CollectionUtil.EMPTY_OBJECT_ARRAY;
+
+ final Object[] packedArgs;
+ if (constrDesc.isVarargs()) {
+ // We have to put all the varargs arguments into a single array argument.
+
+ final Class<?>[] paramTypes = constrDesc.getParamTypes();
+ final int fixedArgCnt = paramTypes.length - 1;
+
+ packedArgs = new Object[fixedArgCnt + 1];
+ for (int i = 0; i < fixedArgCnt; i++) {
+ packedArgs[i] = args[i];
+ }
+
+ final Class<?> compType = paramTypes[fixedArgCnt].getComponentType();
+ final int varArgCnt = args.length - fixedArgCnt;
+ final Object varArgsArray = Array.newInstance(compType, varArgCnt);
+ for (int i = 0; i < varArgCnt; i++) {
+ Array.set(varArgsArray, i, args[fixedArgCnt + i]);
+ }
+ packedArgs[fixedArgCnt] = varArgsArray;
+ } else {
+ packedArgs = args;
+ }
+
+ return constrDesc.invokeConstructor(ow, packedArgs);
+ }
+
+ /**
+ * Contains the common parts of the singleton management for {@link DefaultObjectWrapper} and {@link DefaultObjectWrapper}.
+ *
+ * @param dowSubclassFactory Creates a <em>new</em> read-only object wrapper of the desired
+ * {@link DefaultObjectWrapper} subclass.
+ */
+ public static <OW extends DefaultObjectWrapper, OWC extends DefaultObjectWrapperConfiguration> OW
+ getDefaultObjectWrapperSubclassSingleton(
+ OWC settings,
+ Map<ClassLoader, Map<OWC, WeakReference<OW>>> instanceCache,
+ ReferenceQueue<OW> instanceCacheRefQue,
+ _DefaultObjectWrapperSubclassFactory<OW, OWC> dowSubclassFactory) {
+ // DefaultObjectWrapper can't be cached across different Thread Context Class Loaders (TCCL), because the result of
+ // a class name (String) to Class mappings depends on it, and the staticModels and enumModels need that.
+ // (The ClassIntrospector doesn't have to consider the TCCL, as it only works with Class-es, not class
+ // names.)
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+
+ Reference<OW> instanceRef;
+ Map<OWC, WeakReference<OW>> tcclScopedCache;
+ synchronized (instanceCache) {
+ tcclScopedCache = instanceCache.get(tccl);
+ if (tcclScopedCache == null) {
+ tcclScopedCache = new HashMap<>();
+ instanceCache.put(tccl, tcclScopedCache);
+ instanceRef = null;
+ } else {
+ instanceRef = tcclScopedCache.get(settings);
+ }
+ }
+
+ OW instance = instanceRef != null ? instanceRef.get() : null;
+ if (instance != null) { // cache hit
+ return instance;
+ }
+ // cache miss
+
+ settings = clone(settings); // prevent any aliasing issues
+ instance = dowSubclassFactory.create(settings);
+ if (!instance.isWriteProtected()) {
+ throw new BugException();
+ }
+
+ synchronized (instanceCache) {
+ instanceRef = tcclScopedCache.get(settings);
+ OW concurrentInstance = instanceRef != null ? instanceRef.get() : null;
+ if (concurrentInstance == null) {
+ tcclScopedCache.put(settings, new WeakReference<>(instance, instanceCacheRefQue));
+ } else {
+ instance = concurrentInstance;
+ }
+ }
+
+ removeClearedReferencesFromCache(instanceCache, instanceCacheRefQue);
+
+ return instance;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <BWC extends DefaultObjectWrapperConfiguration> BWC clone(BWC settings) {
+ return (BWC) settings.clone(true);
+ }
+
+ private static <BW extends DefaultObjectWrapper, BWC extends DefaultObjectWrapperConfiguration>
+ void removeClearedReferencesFromCache(
+ Map<ClassLoader, Map<BWC, WeakReference<BW>>> instanceCache,
+ ReferenceQueue<BW> instanceCacheRefQue) {
+ Reference<? extends BW> clearedRef;
+ while ((clearedRef = instanceCacheRefQue.poll()) != null) {
+ synchronized (instanceCache) {
+ findClearedRef: for (Map<BWC, WeakReference<BW>> tcclScopedCache : instanceCache.values()) {
+ for (Iterator<WeakReference<BW>> it2 = tcclScopedCache.values().iterator(); it2.hasNext(); ) {
+ if (it2.next() == clearedRef) {
+ it2.remove();
+ break findClearedRef;
+ }
+ }
+ }
+ } // sync
+ } // while poll
+ }
+
+ /**
+ * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+ */
+ public interface _DefaultObjectWrapperSubclassFactory<BW extends DefaultObjectWrapper, BWC extends DefaultObjectWrapperConfiguration> {
+
+ /** Creates a new read-only {@link DefaultObjectWrapper}; used for {@link DefaultObjectWrapperBuilder} and such. */
+ BW create(BWC sa);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/_ModelImplApi.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_ModelImplApi.java b/src/main/java/org/apache/freemarker/core/model/impl/_ModelImplApi.java
deleted file mode 100644
index 20dd1b7..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/_ModelImplApi.java
+++ /dev/null
@@ -1,37 +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;
-
-/**
- * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
- * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can
- * access things inside this package that users shouldn't.
- */
-public class _ModelImplApi {
-
- private _ModelImplApi() {
- // Not to be instantiated
- }
-
- /** For unit testing only */
- public static void DefaultObjectWrapperFactory_clearInstanceCache() {
- DefaultObjectWrapperBuilder.clearInstanceCache();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java b/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
index 5f52b10..7c2a035 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/_StaticObjectWrappers.java
@@ -19,8 +19,6 @@
package org.apache.freemarker.core.model.impl;
import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder;
/**
* For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
@@ -36,9 +34,6 @@ public final class _StaticObjectWrappers {
public static final DefaultObjectWrapper DEFAULT_OBJECT_WRAPPER
= new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
- public static final BeansWrapper BEANS_WRAPPER
- = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
-
public static final SimpleObjectWrapper SIMPLE_OBJECT_WRAPPER
= new SimpleObjectWrapper(Configuration.VERSION_3_0_0);
{
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/APIModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/APIModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/APIModel.java
deleted file mode 100644
index 3550fe8..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/APIModel.java
+++ /dev/null
@@ -1,45 +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;
-
-/**
- * Exposes the Java API (and properties) of an object.
- *
- * <p>
- * Notes:
- * <ul>
- * <li>The exposing level is inherited from the {@link BeansWrapper}</li>
- * <li>But methods will always shadow properties and fields with identical name, regardless of {@link BeansWrapper}
- * settings</li>
- * </ul>
- *
- * @since 2.3.22
- */
-final class APIModel extends BeanModel {
-
- APIModel(Object object, BeansWrapper wrapper) {
- super(object, wrapper, false);
- }
-
- protected boolean isMethodsShadowItems() {
- return true;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/ArgumentTypes.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ArgumentTypes.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ArgumentTypes.java
deleted file mode 100644
index abac620..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ArgumentTypes.java
+++ /dev/null
@@ -1,647 +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.InvocationTargetException;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.util.BugException;
-import org.apache.freemarker.core.util._ClassUtil;
-
-/**
- * The argument types of a method call; usable as cache key.
- */
-final class ArgumentTypes {
-
- /**
- * Conversion difficulty: Lowest; Java Reflection will do it automatically.
- */
- private static final int CONVERSION_DIFFICULTY_REFLECTION = 0;
-
- /**
- * Conversion difficulty: Medium: Java reflection API won't convert it, FreeMarker has to do it.
- */
- private static final int CONVERSION_DIFFICULTY_FREEMARKER = 1;
-
- /**
- * Conversion difficulty: Highest; conversion is not possible.
- */
- private static final int CONVERSION_DIFFICULTY_IMPOSSIBLE = 2;
-
- /**
- * The types of the arguments; for varags this contains the exploded list (not the array).
- */
- private final Class<?>[] types;
-
- /**
- * @param args The actual arguments. A varargs argument should be present exploded, no as an array.
- */
- ArgumentTypes(Object[] args) {
- int ln = args.length;
- Class<?>[] typesTmp = new Class[ln];
- for (int i = 0; i < ln; ++i) {
- Object arg = args[i];
- typesTmp[i] = arg == null
- ? Null.class
- : arg.getClass();
- }
-
- // `typesTmp` is used so the array is only modified before it's stored in the final `types` field (see JSR-133)
- types = typesTmp;
- }
-
- @Override
- public int hashCode() {
- int hash = 0;
- for (Class<?> type : types) {
- hash ^= type.hashCode();
- }
- return hash;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof ArgumentTypes) {
- ArgumentTypes cs = (ArgumentTypes) o;
- if (cs.types.length != types.length) {
- return false;
- }
- for (int i = 0; i < types.length; ++i) {
- if (cs.types[i] != types[i]) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
- /**
- * @return Possibly {@link EmptyCallableMemberDescriptor#NO_SUCH_METHOD} or
- * {@link EmptyCallableMemberDescriptor#AMBIGUOUS_METHOD}.
- */
- MaybeEmptyCallableMemberDescriptor getMostSpecific(
- List<ReflectionCallableMemberDescriptor> memberDescs, boolean varArg) {
- LinkedList<CallableMemberDescriptor> applicables = getApplicables(memberDescs, varArg);
- if (applicables.isEmpty()) {
- return EmptyCallableMemberDescriptor.NO_SUCH_METHOD;
- }
- if (applicables.size() == 1) {
- return applicables.getFirst();
- }
-
- LinkedList<CallableMemberDescriptor> maximals = new LinkedList<>();
- for (CallableMemberDescriptor applicable : applicables) {
- boolean lessSpecific = false;
- for (Iterator<CallableMemberDescriptor> maximalsIter = maximals.iterator();
- maximalsIter.hasNext(); ) {
- CallableMemberDescriptor maximal = maximalsIter.next();
- final int cmpRes = compareParameterListPreferability(
- applicable.getParamTypes(), maximal.getParamTypes(), varArg);
- if (cmpRes > 0) {
- maximalsIter.remove();
- } else if (cmpRes < 0) {
- lessSpecific = true;
- }
- }
- if (!lessSpecific) {
- maximals.addLast(applicable);
- }
- }
- if (maximals.size() > 1) {
- return EmptyCallableMemberDescriptor.AMBIGUOUS_METHOD;
- }
- return maximals.getFirst();
- }
-
- /**
- * Tells if among the parameter list of two methods, which one fits this argument list better.
- * This method assumes that the parameter lists are applicable to this argument lists; if that's not ensured,
- * what the result will be is undefined.
- *
- * <p>The decision is made by comparing the preferability of each parameter types of the same position in a loop.
- * At the end, the parameter list with the more preferred parameters will be the preferred one. If both parameter
- * lists has the same amount of preferred parameters, the one that has the first (lower index) preferred parameter
- * is the preferred one. Otherwise the two parameter list are considered to be equal in terms of preferability.
- *
- * <p>If there's no numerical conversion involved, the preferability of two parameter types is decided on how
- * specific their types are. For example, {@code String} is more specific than {@link Object} (because
- * {@code Object.class.isAssignableFrom(String.class)}-s), and so {@code String} is preferred. Primitive
- * types are considered to be more specific than the corresponding boxing class (like {@code boolean} is more
- * specific than {@code Boolean}, because the former can't store {@code null}). The preferability decision gets
- * trickier when there's a possibility of numerical conversion from the actual argument type to the type of some of
- * the parameters. If such conversion is only possible for one of the competing parameter types, that parameter
- * automatically wins. If it's possible for both, {@link OverloadedNumberUtil#getArgumentConversionPrice} will
- * be used to calculate the conversion "price", and the parameter type with lowest price wins. There are also
- * a twist with array-to-list and list-to-array conversions; we try to avoid those, so the parameter where such
- * conversion isn't needed will always win.
- *
- * @param paramTypes1 The parameter types of one of the competing methods
- * @param paramTypes2 The parameter types of the other competing method
- * @param varArg Whether these competing methods are varargs methods.
- * @return More than 0 if the first parameter list is preferred, less then 0 if the other is preferred,
- * 0 if there's no decision
- */
- int compareParameterListPreferability(Class<?>[] paramTypes1, Class<?>[] paramTypes2, boolean varArg) {
- final int argTypesLen = types.length;
- final int paramTypes1Len = paramTypes1.length;
- final int paramTypes2Len = paramTypes2.length;
- //assert varArg || paramTypes1Len == paramTypes2Len;
-
- int paramList1WeakWinCnt = 0;
- int paramList2WeakWinCnt = 0;
- int paramList1WinCnt = 0;
- int paramList2WinCnt = 0;
- int paramList1StrongWinCnt = 0;
- int paramList2StrongWinCnt = 0;
- int paramList1VeryStrongWinCnt = 0;
- int paramList2VeryStrongWinCnt = 0;
- int firstWinerParamList = 0;
- for (int i = 0; i < argTypesLen; i++) {
- final Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, i, varArg);
- final Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, i, varArg);
-
- final int winerParam; // 1 => paramType1; -1 => paramType2; 0 => draw
- if (paramType1 == paramType2) {
- winerParam = 0;
- } else {
- final Class<?> argType = types[i];
- final boolean argIsNum = Number.class.isAssignableFrom(argType);
-
- final int numConvPrice1;
- if (argIsNum && _ClassUtil.isNumerical(paramType1)) {
- final Class<?> nonPrimParamType1 = paramType1.isPrimitive()
- ? _ClassUtil.primitiveClassToBoxingClass(paramType1) : paramType1;
- numConvPrice1 = OverloadedNumberUtil.getArgumentConversionPrice(argType, nonPrimParamType1);
- } else {
- numConvPrice1 = Integer.MAX_VALUE;
- }
- // numConvPrice1 is Integer.MAX_VALUE if either:
- // - argType and paramType1 aren't both numerical
- // - FM doesn't know some of the numerical types, or the conversion between them is not allowed
-
- final int numConvPrice2;
- if (argIsNum && _ClassUtil.isNumerical(paramType2)) {
- final Class<?> nonPrimParamType2 = paramType2.isPrimitive()
- ? _ClassUtil.primitiveClassToBoxingClass(paramType2) : paramType2;
- numConvPrice2 = OverloadedNumberUtil.getArgumentConversionPrice(argType, nonPrimParamType2);
- } else {
- numConvPrice2 = Integer.MAX_VALUE;
- }
-
- if (numConvPrice1 == Integer.MAX_VALUE) {
- if (numConvPrice2 == Integer.MAX_VALUE) { // No numerical conversions anywhere
- // List to array conversions (unwrapping sometimes makes a List instead of an array)
- if (List.class.isAssignableFrom(argType)
- && (paramType1.isArray() || paramType2.isArray())) {
- if (paramType1.isArray()) {
- if (paramType2.isArray()) { // both paramType1 and paramType2 are arrays
- int r = compareParameterListPreferability_cmpTypeSpecificty(
- paramType1.getComponentType(), paramType2.getComponentType());
- // Because we don't know if the List items are instances of the component
- // type or not, we prefer the safer choice, which is the more generic array:
- if (r > 0) {
- winerParam = 2;
- paramList2StrongWinCnt++;
- } else if (r < 0) {
- winerParam = 1;
- paramList1StrongWinCnt++;
- } else {
- winerParam = 0;
- }
- } else { // paramType1 is array, paramType2 isn't
- // Avoid List to array conversion if the other way makes any sense:
- if (Collection.class.isAssignableFrom(paramType2)) {
- winerParam = 2;
- paramList2StrongWinCnt++;
- } else {
- winerParam = 1;
- paramList1WeakWinCnt++;
- }
- }
- } else { // paramType2 is array, paramType1 isn't
- // Avoid List to array conversion if the other way makes any sense:
- if (Collection.class.isAssignableFrom(paramType1)) {
- winerParam = 1;
- paramList1StrongWinCnt++;
- } else {
- winerParam = 2;
- paramList2WeakWinCnt++;
- }
- }
- } else if (argType.isArray()
- && (List.class.isAssignableFrom(paramType1)
- || List.class.isAssignableFrom(paramType2))) {
- // Array to List conversions (unwrapping sometimes makes an array instead of a List)
- if (List.class.isAssignableFrom(paramType1)) {
- if (List.class.isAssignableFrom(paramType2)) {
- // Both paramType1 and paramType2 extends List
- winerParam = 0;
- } else {
- // Only paramType1 extends List
- winerParam = 2;
- paramList2VeryStrongWinCnt++;
- }
- } else {
- // Only paramType2 extends List
- winerParam = 1;
- paramList1VeryStrongWinCnt++;
- }
- } else { // No list to/from array conversion
- final int r = compareParameterListPreferability_cmpTypeSpecificty(
- paramType1, paramType2);
- if (r > 0) {
- winerParam = 1;
- if (r > 1) {
- paramList1WinCnt++;
- } else {
- paramList1WeakWinCnt++;
- }
- } else if (r < 0) {
- winerParam = -1;
- if (r < -1) {
- paramList2WinCnt++;
- } else {
- paramList2WeakWinCnt++;
- }
- } else {
- winerParam = 0;
- }
- }
- } else { // No num. conv. of param1, num. conv. of param2
- winerParam = -1;
- paramList2WinCnt++;
- }
- } else if (numConvPrice2 == Integer.MAX_VALUE) { // Num. conv. of param1, not of param2
- winerParam = 1;
- paramList1WinCnt++;
- } else { // Num. conv. of both param1 and param2
- if (numConvPrice1 != numConvPrice2) {
- if (numConvPrice1 < numConvPrice2) {
- winerParam = 1;
- if (numConvPrice1 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE
- && numConvPrice2 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) {
- paramList1StrongWinCnt++;
- } else {
- paramList1WinCnt++;
- }
- } else {
- winerParam = -1;
- if (numConvPrice2 < OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE
- && numConvPrice1 > OverloadedNumberUtil.BIG_MANTISSA_LOSS_PRICE) {
- paramList2StrongWinCnt++;
- } else {
- paramList2WinCnt++;
- }
- }
- } else {
- winerParam = (paramType1.isPrimitive() ? 1 : 0) - (paramType2.isPrimitive() ? 1 : 0);
- if (winerParam == 1) paramList1WeakWinCnt++;
- else if (winerParam == -1) paramList2WeakWinCnt++;
- }
- }
- } // when paramType1 != paramType2
-
- if (firstWinerParamList == 0 && winerParam != 0) {
- firstWinerParamList = winerParam;
- }
- } // for each parameter types
-
- if (paramList1VeryStrongWinCnt != paramList2VeryStrongWinCnt) {
- return paramList1VeryStrongWinCnt - paramList2VeryStrongWinCnt;
- } else if (paramList1StrongWinCnt != paramList2StrongWinCnt) {
- return paramList1StrongWinCnt - paramList2StrongWinCnt;
- } else if (paramList1WinCnt != paramList2WinCnt) {
- return paramList1WinCnt - paramList2WinCnt;
- } else if (paramList1WeakWinCnt != paramList2WeakWinCnt) {
- return paramList1WeakWinCnt - paramList2WeakWinCnt;
- } else if (firstWinerParamList != 0) { // paramList1WinCnt == paramList2WinCnt
- return firstWinerParamList;
- } else { // still undecided
- if (varArg) {
- if (paramTypes1Len == paramTypes2Len) {
- // If we had a 0-length varargs array in both methods, we also compare the types at the
- // index of the varargs parameter, like if we had a single varargs argument. However, this
- // time we don't have an argument type, so we can only decide based on type specificity:
- if (argTypesLen == paramTypes1Len - 1) {
- Class<?> paramType1 = getParamType(paramTypes1, paramTypes1Len, argTypesLen, true);
- Class<?> paramType2 = getParamType(paramTypes2, paramTypes2Len, argTypesLen, true);
- if (_ClassUtil.isNumerical(paramType1) && _ClassUtil.isNumerical(paramType2)) {
- int r = OverloadedNumberUtil.compareNumberTypeSpecificity(paramType1, paramType2);
- if (r != 0) return r;
- // falls through
- }
- return compareParameterListPreferability_cmpTypeSpecificty(paramType1, paramType2);
- } else {
- return 0;
- }
- } else {
- // The method with more oms parameters wins:
- return paramTypes1Len - paramTypes2Len;
- }
- } else {
- return 0;
- }
- }
- }
-
- /**
- * Trivial comparison of type specificities; unaware of numerical conversions.
- *
- * @return Less-than-0, 0, or more-than-0 depending on which side is more specific. The absolute value is 1 if
- * the difference is only in primitive VS non-primitive, more otherwise.
- */
- private int compareParameterListPreferability_cmpTypeSpecificty(
- final Class<?> paramType1, final Class<?> paramType2) {
- // The more specific (smaller) type wins.
-
- final Class<?> nonPrimParamType1 = paramType1.isPrimitive()
- ? _ClassUtil.primitiveClassToBoxingClass(paramType1) : paramType1;
- final Class<?> nonPrimParamType2 = paramType2.isPrimitive()
- ? _ClassUtil.primitiveClassToBoxingClass(paramType2) : paramType2;
-
- if (nonPrimParamType1 == nonPrimParamType2) {
- if (nonPrimParamType1 != paramType1) {
- if (nonPrimParamType2 != paramType2) {
- return 0; // identical prim. types; shouldn't ever be reached
- } else {
- return 1; // param1 is prim., param2 is non prim.
- }
- } else if (nonPrimParamType2 != paramType2) {
- return -1; // param1 is non-prim., param2 is prim.
- } else {
- return 0; // identical non-prim. types
- }
- } else if (nonPrimParamType2.isAssignableFrom(nonPrimParamType1)) {
- return 2;
- } else if (nonPrimParamType1.isAssignableFrom(nonPrimParamType2)) {
- return -2;
- } if (nonPrimParamType1 == Character.class && nonPrimParamType2.isAssignableFrom(String.class)) {
- return 2; // A character is a 1 long string in FTL, so we pretend that it's a String subtype.
- } if (nonPrimParamType2 == Character.class && nonPrimParamType1.isAssignableFrom(String.class)) {
- return -2;
- } else {
- return 0; // unrelated types
- }
- }
-
- private static Class<?> getParamType(Class<?>[] paramTypes, int paramTypesLen, int i, boolean varArg) {
- return varArg && i >= paramTypesLen - 1
- ? paramTypes[paramTypesLen - 1].getComponentType()
- : paramTypes[i];
- }
-
- /**
- * Returns all methods that are applicable to actual
- * parameter types represented by this ArgumentTypes object.
- */
- LinkedList<CallableMemberDescriptor> getApplicables(
- List<ReflectionCallableMemberDescriptor> memberDescs, boolean varArg) {
- LinkedList<CallableMemberDescriptor> applicables = new LinkedList<>();
- for (ReflectionCallableMemberDescriptor memberDesc : memberDescs) {
- int difficulty = isApplicable(memberDesc, varArg);
- if (difficulty != CONVERSION_DIFFICULTY_IMPOSSIBLE) {
- if (difficulty == CONVERSION_DIFFICULTY_REFLECTION) {
- applicables.add(memberDesc);
- } else if (difficulty == CONVERSION_DIFFICULTY_FREEMARKER) {
- applicables.add(new SpecialConversionCallableMemberDescriptor(memberDesc));
- } else {
- throw new BugException();
- }
- }
- }
- return applicables;
- }
-
- /**
- * Returns if the supplied method is applicable to actual
- * parameter types represented by this ArgumentTypes object, also tells
- * how difficult that conversion is.
- *
- * @return One of the <tt>CONVERSION_DIFFICULTY_...</tt> constants.
- */
- private int isApplicable(ReflectionCallableMemberDescriptor memberDesc, boolean varArg) {
- final Class<?>[] paramTypes = memberDesc.getParamTypes();
- final int cl = types.length;
- final int fl = paramTypes.length - (varArg ? 1 : 0);
- if (varArg) {
- if (cl < fl) {
- return CONVERSION_DIFFICULTY_IMPOSSIBLE;
- }
- } else {
- if (cl != fl) {
- return CONVERSION_DIFFICULTY_IMPOSSIBLE;
- }
- }
-
- int maxDifficulty = 0;
- for (int i = 0; i < fl; ++i) {
- int difficulty = isMethodInvocationConvertible(paramTypes[i], types[i]);
- if (difficulty == CONVERSION_DIFFICULTY_IMPOSSIBLE) {
- return CONVERSION_DIFFICULTY_IMPOSSIBLE;
- }
- if (maxDifficulty < difficulty) {
- maxDifficulty = difficulty;
- }
- }
- if (varArg) {
- Class<?> varArgParamType = paramTypes[fl].getComponentType();
- for (int i = fl; i < cl; ++i) {
- int difficulty = isMethodInvocationConvertible(varArgParamType, types[i]);
- if (difficulty == CONVERSION_DIFFICULTY_IMPOSSIBLE) {
- return CONVERSION_DIFFICULTY_IMPOSSIBLE;
- }
- if (maxDifficulty < difficulty) {
- maxDifficulty = difficulty;
- }
- }
- }
- return maxDifficulty;
- }
-
- /**
- * Determines whether a type is convertible to another type via
- * method invocation conversion, and if so, what kind of conversion is needed.
- * It treates the object type counterpart of primitive types as if they were the primitive types
- * (that is, a Boolean actual parameter type matches boolean primitive formal type). This behavior
- * is because this method is used to determine applicable methods for
- * an actual parameter list, and primitive types are represented by
- * their object duals in reflective method calls.
- * @param formal the parameter type to which the actual
- * parameter type should be convertible; possibly a primitive type
- * @param actual the argument type; not a primitive type, maybe {@link Null}.
- *
- * @return One of the <tt>CONVERSION_DIFFICULTY_...</tt> constants.
- */
- private int isMethodInvocationConvertible(final Class<?> formal, final Class<?> actual) {
- // Check for identity or widening reference conversion
- if (formal.isAssignableFrom(actual) && actual != CharacterOrString.class) {
- return CONVERSION_DIFFICULTY_REFLECTION;
- } else {
- final Class<?> formalNP;
- if (formal.isPrimitive()) {
- if (actual == Null.class) {
- return CONVERSION_DIFFICULTY_IMPOSSIBLE;
- }
-
- formalNP = _ClassUtil.primitiveClassToBoxingClass(formal);
- if (actual == formalNP) {
- // Character and char, etc.
- return CONVERSION_DIFFICULTY_REFLECTION;
- }
- } else { // formal is non-primitive
- if (actual == Null.class) {
- return CONVERSION_DIFFICULTY_REFLECTION;
- }
-
- formalNP = formal;
- }
- if (Number.class.isAssignableFrom(actual) && Number.class.isAssignableFrom(formalNP)) {
- return OverloadedNumberUtil.getArgumentConversionPrice(actual, formalNP) == Integer.MAX_VALUE
- ? CONVERSION_DIFFICULTY_IMPOSSIBLE : CONVERSION_DIFFICULTY_REFLECTION;
- } else if (formal.isArray()) {
- // BeansWrapper method/constructor calls convert from List to array automatically
- return List.class.isAssignableFrom(actual)
- ? CONVERSION_DIFFICULTY_FREEMARKER : CONVERSION_DIFFICULTY_IMPOSSIBLE;
- } else if (actual.isArray() && formal.isAssignableFrom(List.class)) {
- // BeansWrapper method/constructor calls convert from array to List automatically
- return CONVERSION_DIFFICULTY_FREEMARKER;
- } else if (actual == CharacterOrString.class
- && (formal.isAssignableFrom(String.class)
- || formal.isAssignableFrom(Character.class) || formal == char.class)) {
- return CONVERSION_DIFFICULTY_FREEMARKER;
- } else {
- return CONVERSION_DIFFICULTY_IMPOSSIBLE;
- }
- }
- }
-
- /**
- * Symbolizes the class of null (it's missing from Java).
- */
- private static class Null {
-
- // Can't be instantiated
- private Null() { }
-
- }
-
- /**
- * Used instead of {@link ReflectionCallableMemberDescriptor} when the method is only applicable
- * ({@link #isApplicable}) with conversion that Java reflection won't do. It delegates to a
- * {@link ReflectionCallableMemberDescriptor}, but it adds the necessary conversions to the invocation methods.
- */
- private static final class SpecialConversionCallableMemberDescriptor extends CallableMemberDescriptor {
-
- private final ReflectionCallableMemberDescriptor callableMemberDesc;
-
- SpecialConversionCallableMemberDescriptor(ReflectionCallableMemberDescriptor callableMemberDesc) {
- this.callableMemberDesc = callableMemberDesc;
- }
-
- @Override
- TemplateModel invokeMethod(BeansWrapper bw, Object obj, Object[] args) throws TemplateModelException,
- InvocationTargetException, IllegalAccessException {
- convertArgsToReflectionCompatible(bw, args);
- return callableMemberDesc.invokeMethod(bw, obj, args);
- }
-
- @Override
- Object invokeConstructor(BeansWrapper bw, Object[] args) throws IllegalArgumentException,
- InstantiationException, IllegalAccessException, InvocationTargetException, TemplateModelException {
- convertArgsToReflectionCompatible(bw, args);
- return callableMemberDesc.invokeConstructor(bw, args);
- }
-
- @Override
- String getDeclaration() {
- return callableMemberDesc.getDeclaration();
- }
-
- @Override
- boolean isConstructor() {
- return callableMemberDesc.isConstructor();
- }
-
- @Override
- boolean isStatic() {
- return callableMemberDesc.isStatic();
- }
-
- @Override
- boolean isVarargs() {
- return callableMemberDesc.isVarargs();
- }
-
- @Override
- Class<?>[] getParamTypes() {
- return callableMemberDesc.getParamTypes();
- }
-
- @Override
- String getName() {
- return callableMemberDesc.getName();
- }
-
- private void convertArgsToReflectionCompatible(BeansWrapper bw, Object[] args) throws TemplateModelException {
- Class<?>[] paramTypes = callableMemberDesc.getParamTypes();
- int ln = paramTypes.length;
- for (int i = 0; i < ln; i++) {
- Class<?> paramType = paramTypes[i];
- final Object arg = args[i];
- if (arg == null) continue;
-
- // Handle conversion between List and array types, in both directions. Java reflection won't do such
- // conversion, so we have to.
- // Most reflection-incompatible conversions were already addressed by the unwrapping. The reason
- // this one isn't is that for overloaded methods the hint of a given parameter position is often vague,
- // so we may end up with a List even if some parameter types at that position are arrays (remember, we
- // have to chose one unwrapping target type, despite that we have many possible overloaded methods), or
- // the other way around (that happens when AdapterTemplateMoldel returns an array).
- // Later, the overloaded method selection will assume that a List argument is applicable to an array
- // parameter, and that an array argument is applicable to a List parameter, so we end up with this
- // situation.
- if (paramType.isArray() && arg instanceof List) {
- args[i] = bw.listToArray((List<?>) arg, paramType, null);
- }
- if (arg.getClass().isArray() && paramType.isAssignableFrom(List.class)) {
- args[i] = bw.arrayToList(arg);
- }
-
- // Handle the conversion from CharacterOrString to Character or String:
- if (arg instanceof CharacterOrString) {
- if (paramType == Character.class || paramType == char.class
- || (!paramType.isAssignableFrom(String.class)
- && paramType.isAssignableFrom(Character.class))) {
- args[i] = Character.valueOf(((CharacterOrString) arg).getAsChar());
- } else {
- args[i] = ((CharacterOrString) arg).getAsString();
- }
- }
- }
- }
-
- }
-
-}
\ 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/ArrayModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ArrayModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ArrayModel.java
deleted file mode 100644
index 8be341c..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ArrayModel.java
+++ /dev/null
@@ -1,126 +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.Array;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-
-/**
- * <p>A class that will wrap an arbitrary array into {@link TemplateCollectionModel}
- * and {@link TemplateSequenceModel} interfaces. It supports element retrieval through the <tt>array[index]</tt>
- * syntax and can be iterated as a list.
- */
-public class ArrayModel
-extends
- BeanModel
-implements
- TemplateCollectionModel,
- TemplateSequenceModel {
- static final ModelFactory FACTORY =
- new ModelFactory()
- {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new ArrayModel(object, (BeansWrapper) wrapper);
- }
- };
-
- // Cached length of the array
- private final int length;
-
- /**
- * Creates a new model that wraps the specified array object.
- * @param array the array object to wrap into a model.
- * @param wrapper the {@link BeansWrapper} associated with this model.
- * Every model has to have an associated {@link BeansWrapper} instance. The
- * model gains many attributes from its wrapper, including the caching
- * behavior, method exposure level, method-over-item shadowing policy etc.
- * @throws IllegalArgumentException if the passed object is not a Java array.
- */
- public ArrayModel(Object array, BeansWrapper wrapper) {
- super(array, wrapper);
- Class clazz = array.getClass();
- if (!clazz.isArray())
- throw new IllegalArgumentException("Object is not an array, it's " + array.getClass().getName());
- length = Array.getLength(array);
- }
-
-
- @Override
- public TemplateModelIterator iterator() {
- return new Iterator();
- }
-
- @Override
- public TemplateModel get(int index)
- throws TemplateModelException {
- try {
- return wrap(Array.get(object, index));
- } catch (IndexOutOfBoundsException e) {
- return null;
-// throw new TemplateModelException("Index out of bounds: " + index);
- }
- }
-
- private class Iterator
- implements
- TemplateSequenceModel,
- TemplateModelIterator {
- private int position = 0;
-
- @Override
- public boolean hasNext() {
- return position < length;
- }
-
- @Override
- public TemplateModel get(int index)
- throws TemplateModelException {
- return ArrayModel.this.get(index);
- }
-
- @Override
- public TemplateModel next()
- throws TemplateModelException {
- return position < length ? get(position++) : null;
- }
-
- @Override
- public int size() {
- return ArrayModel.this.size();
- }
- }
-
- @Override
- public int size() {
- return length;
- }
-
- @Override
- public boolean isEmpty() {
- return length == 0;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/BeanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeanModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeanModel.java
deleted file mode 100644
index 5c2db2b..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeanModel.java
+++ /dev/null
@@ -1,365 +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.beans.IndexedPropertyDescriptor;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core._DelayedFTLTypeDescription;
-import org.apache.freemarker.core._DelayedJQuote;
-import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.AdapterTemplateModel;
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.WrapperTemplateModel;
-import org.apache.freemarker.core.model.impl.CollectionAndSequence;
-import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
-import org.apache.freemarker.core.util._StringUtil;
-import org.slf4j.Logger;
-
-/**
- * A class that will wrap an arbitrary object into {@link org.apache.freemarker.core.model.TemplateHashModel}
- * interface allowing calls to arbitrary property getters and invocation of
- * accessible methods on the object from a template using the
- * <tt>object.foo</tt> to access properties and <tt>object.bar(arg1, arg2)</tt> to
- * invoke methods on it. You can also use the <tt>object.foo[index]</tt> syntax to
- * access indexed properties. It uses Beans {@link java.beans.Introspector}
- * to dynamically discover the properties and methods.
- */
-
-public class BeanModel
-implements
- TemplateHashModelEx, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport {
-
- private static final Logger LOG = _CoreLogs.BEANS_WRAPPER;
-
- protected final Object object;
- protected final BeansWrapper wrapper;
-
- // We use this to represent an unknown value as opposed to known value of null (JR)
- static final TemplateModel UNKNOWN = new SimpleScalar("UNKNOWN");
-
- static final ModelFactory FACTORY =
- new ModelFactory()
- {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new BeanModel(object, (BeansWrapper) wrapper);
- }
- };
-
- // I've tried to use a volatile ConcurrentHashMap field instead of HashMap + synchronized(this), but oddly it was
- // a bit slower, at least on Java 8 u66.
- private HashMap<Object, TemplateModel> memberCache;
-
- /**
- * Creates a new model that wraps the specified object. Note that there are
- * specialized subclasses of this class for wrapping arrays, collections,
- * enumeration, iterators, and maps. Note also that the superclass can be
- * used to wrap String objects if only scalar functionality is needed. You
- * can also choose to delegate the choice over which model class is used for
- * wrapping to {@link BeansWrapper#wrap(Object)}.
- * @param object the object to wrap into a model.
- * @param wrapper the {@link BeansWrapper} associated with this model.
- * Every model has to have an associated {@link BeansWrapper} instance. The
- * model gains many attributes from its wrapper, including the caching
- * behavior, method exposure level, method-over-item shadowing policy etc.
- */
- public BeanModel(Object object, BeansWrapper wrapper) {
- // [2.4]: All models were introspected here, then the results was discareded, and get() will just do the
- // introspection again. So is this necessary? (The inrospectNow parameter was added in 2.3.21 to allow
- // lazy-introspecting BeansWrapper.trueModel|falseModel.)
- this(object, wrapper, true);
- }
-
- /** @since 2.3.21 */
- BeanModel(Object object, BeansWrapper wrapper, boolean inrospectNow) {
- this.object = object;
- this.wrapper = wrapper;
- if (inrospectNow && object != null) {
- // [2.4]: Could this be removed?
- wrapper.getClassIntrospector().get(object.getClass());
- }
- }
-
- /**
- * Uses Beans introspection to locate a property or method with name
- * matching the key name. If a method or property is found, it's wrapped
- * into {@link org.apache.freemarker.core.model.TemplateMethodModelEx} (for a method or
- * indexed property), or evaluated on-the-fly and the return value wrapped
- * into appropriate model (for a simple property) Models for various
- * properties and methods are cached on a per-class basis, so the costly
- * introspection is performed only once per property or method of a class.
- * (Side-note: this also implies that any class whose method has been called
- * will be strongly referred to by the framework and will not become
- * unloadable until this class has been unloaded first. Normally this is not
- * an issue, but can be in a rare scenario where you create many classes on-
- * the-fly. Also, as the cache grows with new classes and methods introduced
- * to the framework, it may appear as if it were leaking memory. The
- * framework does, however detect class reloads (if you happen to be in an
- * environment that does this kind of things--servlet containers do it when
- * they reload a web application) and flushes the cache. If no method or
- * property matching the key is found, the framework will try to invoke
- * methods with signature
- * <tt>non-void-return-type get(java.lang.String)</tt>,
- * then <tt>non-void-return-type get(java.lang.Object)</tt>, or
- * alternatively (if the wrapped object is a resource bundle)
- * <tt>Object getObject(java.lang.String)</tt>.
- * @throws TemplateModelException if there was no property nor method nor
- * a generic <tt>get</tt> method to invoke.
- */
- @Override
- public TemplateModel get(String key)
- throws TemplateModelException {
- Class<?> clazz = object.getClass();
- Map<Object, Object> classInfo = wrapper.getClassIntrospector().get(clazz);
- TemplateModel retval = null;
-
- try {
- if (wrapper.isMethodsShadowItems()) {
- Object fd = classInfo.get(key);
- if (fd != null) {
- retval = invokeThroughDescriptor(fd, classInfo);
- } else {
- retval = invokeGenericGet(classInfo, clazz, key);
- }
- } else {
- TemplateModel model = invokeGenericGet(classInfo, clazz, key);
- final TemplateModel nullModel = wrapper.wrap(null);
- if (model != nullModel && model != UNKNOWN) {
- return model;
- }
- Object fd = classInfo.get(key);
- if (fd != null) {
- retval = invokeThroughDescriptor(fd, classInfo);
- if (retval == UNKNOWN && model == nullModel) {
- // This is the (somewhat subtle) case where the generic get() returns null
- // and we have no bean info, so we respect the fact that
- // the generic get() returns null and return null. (JR)
- retval = nullModel;
- }
- }
- }
- if (retval == UNKNOWN) {
- if (wrapper.isStrict()) {
- throw new InvalidPropertyException("No such bean property: " + key);
- } else {
- logNoSuchKey(key, classInfo);
- }
- retval = wrapper.wrap(null);
- }
- return retval;
- } catch (TemplateModelException e) {
- throw e;
- } catch (Exception e) {
- throw new _TemplateModelException(e,
- "An error has occurred when reading existing sub-variable ", new _DelayedJQuote(key),
- "; see cause exception! The type of the containing value was: ",
- new _DelayedFTLTypeDescription(this)
- );
- }
- }
-
- private void logNoSuchKey(String key, Map<?, ?> keyMap) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Key " + _StringUtil.jQuoteNoXSS(key) + " was not found on instance of " +
- object.getClass().getName() + ". Introspection information for " +
- "the class is: " + keyMap);
- }
- }
-
- /**
- * Whether the model has a plain get(String) or get(Object) method
- */
-
- protected boolean hasPlainGetMethod() {
- return wrapper.getClassIntrospector().get(object.getClass()).get(ClassIntrospector.GENERIC_GET_KEY) != null;
- }
-
- private TemplateModel invokeThroughDescriptor(Object desc, Map<Object, Object> classInfo)
- throws IllegalAccessException, InvocationTargetException, TemplateModelException {
- // See if this particular instance has a cached implementation for the requested feature descriptor
- TemplateModel cachedModel;
- synchronized (this) {
- cachedModel = memberCache != null ? memberCache.get(desc) : null;
- }
-
- if (cachedModel != null) {
- return cachedModel;
- }
-
- TemplateModel resultModel = UNKNOWN;
- if (desc instanceof IndexedPropertyDescriptor) {
- Method readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod();
- resultModel = cachedModel =
- new SimpleMethodModel(object, readMethod,
- ClassIntrospector.getArgTypes(classInfo, readMethod), wrapper);
- } else if (desc instanceof PropertyDescriptor) {
- PropertyDescriptor pd = (PropertyDescriptor) desc;
- resultModel = wrapper.invokeMethod(object, pd.getReadMethod(), null);
- // cachedModel remains null, as we don't cache these
- } else if (desc instanceof Field) {
- resultModel = wrapper.wrap(((Field) desc).get(object));
- // cachedModel remains null, as we don't cache these
- } else if (desc instanceof Method) {
- Method method = (Method) desc;
- resultModel = cachedModel = new SimpleMethodModel(
- object, method, ClassIntrospector.getArgTypes(classInfo, method), wrapper);
- } else if (desc instanceof OverloadedMethods) {
- resultModel = cachedModel = new OverloadedMethodsModel(
- object, (OverloadedMethods) desc, wrapper);
- }
-
- // If new cachedModel was created, cache it
- if (cachedModel != null) {
- synchronized (this) {
- if (memberCache == null) {
- memberCache = new HashMap<>();
- }
- memberCache.put(desc, cachedModel);
- }
- }
- return resultModel;
- }
-
- void clearMemberCache() {
- synchronized (this) {
- memberCache = null;
- }
- }
-
- protected TemplateModel invokeGenericGet(Map/*<Object, Object>*/ classInfo, Class<?> clazz, String key)
- throws IllegalAccessException, InvocationTargetException,
- TemplateModelException {
- Method genericGet = (Method) classInfo.get(ClassIntrospector.GENERIC_GET_KEY);
- if (genericGet == null) {
- return UNKNOWN;
- }
-
- return wrapper.invokeMethod(object, genericGet, new Object[] { key });
- }
-
- protected TemplateModel wrap(Object obj)
- throws TemplateModelException {
- return wrapper.getOuterIdentity().wrap(obj);
- }
-
- protected Object unwrap(TemplateModel model)
- throws TemplateModelException {
- return wrapper.unwrap(model);
- }
-
- /**
- * Tells whether the model is considered to be empty.
- * It is empty if the wrapped object is a 0 length {@link String}, or an empty {@link Collection} or and empty
- * {@link Map}, or an {@link Iterator} that has no more items, or a {@link Boolean#FALSE}, or {@code null}.
- */
- @Override
- public boolean isEmpty() {
- if (object instanceof String) {
- return ((String) object).length() == 0;
- }
- if (object instanceof Collection) {
- return ((Collection<?>) object).isEmpty();
- }
- if (object instanceof Iterator) {
- return !((Iterator<?>) object).hasNext();
- }
- if (object instanceof Map) {
- return ((Map<?,?>) object).isEmpty();
- }
- // [FM3] Why's FALSE empty?
- return object == null || Boolean.FALSE.equals(object);
- }
-
- /**
- * Returns the same as {@link #getWrappedObject()}; to ensure that, this method will be final starting from 2.4.
- * This behavior of {@link BeanModel} is assumed by some FreeMarker code.
- */
- @Override
- public Object getAdaptedObject(Class<?> hint) {
- return object; // return getWrappedObject(); starting from 2.4
- }
-
- @Override
- public Object getWrappedObject() {
- return object;
- }
-
- @Override
- public int size() {
- return wrapper.getClassIntrospector().keyCount(object.getClass());
- }
-
- @Override
- public TemplateCollectionModel keys() {
- return new CollectionAndSequence(new SimpleSequence(keySet(), wrapper));
- }
-
- @Override
- public TemplateCollectionModel values() throws TemplateModelException {
- List<Object> values = new ArrayList<>(size());
- TemplateModelIterator it = keys().iterator();
- while (it.hasNext()) {
- String key = ((TemplateScalarModel) it.next()).getAsString();
- values.add(get(key));
- }
- return new CollectionAndSequence(new SimpleSequence(values, wrapper));
- }
-
- @Override
- public String toString() {
- return object.toString();
- }
-
- /**
- * Helper method to support TemplateHashModelEx. Returns the Set of
- * Strings which are available via the TemplateHashModel
- * interface. Subclasses that override <tt>invokeGenericGet</tt> to
- * provide additional hash keys should also override this method.
- */
- protected Set/*<Object>*/ keySet() {
- return wrapper.getClassIntrospector().keySet(object.getClass());
- }
-
- @Override
- public TemplateModel getAPI() throws TemplateModelException {
- return wrapper.wrapAsAPI(object);
- }
-
-}
\ 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/BeansModelCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansModelCache.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansModelCache.java
deleted file mode 100644
index 52db93d..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansModelCache.java
+++ /dev/null
@@ -1,73 +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.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.freemarker.core.model.TemplateModel;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-public class BeansModelCache extends ModelCache {
- private final Map classToFactory = new ConcurrentHashMap();
- private final Set mappedClassNames = new HashSet();
-
- private final BeansWrapper wrapper;
-
- BeansModelCache(BeansWrapper wrapper) {
- this.wrapper = wrapper;
- }
-
- @Override
- protected boolean isCacheable(Object object) {
- return object.getClass() != Boolean.class;
- }
-
- @Override
- @SuppressFBWarnings(value="JLM_JSR166_UTILCONCURRENT_MONITORENTER", justification="Locks for factory creation only")
- protected TemplateModel create(Object object) {
- Class clazz = object.getClass();
-
- ModelFactory factory = (ModelFactory) classToFactory.get(clazz);
-
- if (factory == null) {
- // Synchronized so that we won't unnecessarily create the same factory for multiple times in parallel.
- synchronized (classToFactory) {
- factory = (ModelFactory) classToFactory.get(clazz);
- if (factory == null) {
- String className = clazz.getName();
- // clear mappings when class reloading is detected
- if (!mappedClassNames.add(className)) {
- classToFactory.clear();
- mappedClassNames.clear();
- mappedClassNames.add(className);
- }
- factory = wrapper.getModelFactory(clazz);
- classToFactory.put(clazz, factory);
- }
- }
- }
-
- return factory.create(object, wrapper);
- }
-}
[16/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
new file mode 100644
index 0000000..204ff1f
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsModel.java
@@ -0,0 +1,82 @@
+/*
+ * 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.util.Collections;
+import java.util.List;
+
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+
+/**
+ * Wraps a set of same-name overloaded methods behind {@link org.apache.freemarker.core.model.TemplateMethodModel} interface,
+ * like if it was a single method, chooses among them behind the scenes on call-time based on the argument values.
+ */
+public class OverloadedMethodsModel
+implements
+ TemplateMethodModelEx,
+ TemplateSequenceModel {
+ private final Object object;
+ private final OverloadedMethods overloadedMethods;
+ private final DefaultObjectWrapper wrapper;
+
+ OverloadedMethodsModel(Object object, OverloadedMethods overloadedMethods, DefaultObjectWrapper wrapper) {
+ this.object = object;
+ this.overloadedMethods = overloadedMethods;
+ this.wrapper = wrapper;
+ }
+
+ /**
+ * Invokes the method, passing it the arguments from the list. The actual
+ * method to call from several overloaded methods will be chosen based
+ * on the classes of the arguments.
+ * @throws TemplateModelException if the method cannot be chosen
+ * unambiguously.
+ */
+ @Override
+ public Object exec(List arguments)
+ throws TemplateModelException {
+ MemberAndArguments maa = overloadedMethods.getMemberAndArguments(arguments, wrapper);
+ try {
+ return maa.invokeMethod(wrapper, object);
+ } catch (Exception e) {
+ if (e instanceof TemplateModelException) throw (TemplateModelException) e;
+
+ throw _MethodUtil.newInvocationTemplateModelException(
+ object,
+ maa.getCallableMemberDescriptor(),
+ e);
+ }
+ }
+
+ @Override
+ public TemplateModel get(int index) throws TemplateModelException {
+ return (TemplateModel) exec(Collections.singletonList(
+ new SimpleNumber(Integer.valueOf(index))));
+ }
+
+ @Override
+ public int size() throws TemplateModelException {
+ throw new TemplateModelException("?size is unsupported for " + getClass().getName());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java
new file mode 100644
index 0000000..0c10a6b
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethodsSubset.java
@@ -0,0 +1,402 @@
+/*
+ * 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.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util._ClassUtil;
+import org.apache.freemarker.core.util._NullArgumentException;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Encapsulates the rules and data structures (including cache) for choosing of the best matching callable member for
+ * a parameter list, from a given set of callable members. There are two subclasses of this, one for non-varags methods,
+ * and one for varargs methods.
+ */
+abstract class OverloadedMethodsSubset {
+
+ /**
+ * Used for an optimization trick to substitute an array of whatever size that contains only 0-s. Since this array
+ * is 0 long, this means that the code that reads the int[] always have to check if the int[] has this value, and
+ * then it has to act like if was all 0-s.
+ */
+ static final int[] ALL_ZEROS_ARRAY = new int[0];
+
+ private static final int[][] ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY = new int[1][];
+ static {
+ ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY[0] = ALL_ZEROS_ARRAY;
+ }
+
+ private Class[/*number of args*/][/*arg index*/] unwrappingHintsByParamCount;
+
+ /**
+ * Tells what types occur at a given parameter position with a bit field. See {@link TypeFlags}.
+ */
+ private int[/*number of args*/][/*arg index*/] typeFlagsByParamCount;
+
+ // TODO: This can cause memory-leak when classes are re-loaded. However, first the genericClassIntrospectionCache
+ // and such need to be oms in this regard.
+ private final Map/*<ArgumentTypes, MaybeEmptyCallableMemberDescriptor>*/ argTypesToMemberDescCache
+ = new ConcurrentHashMap(6, 0.75f, 1);
+
+ private final List/*<ReflectionCallableMemberDescriptor>*/ memberDescs = new LinkedList();
+
+ OverloadedMethodsSubset() {
+ //
+ }
+
+ void addCallableMemberDescriptor(ReflectionCallableMemberDescriptor memberDesc) {
+ memberDescs.add(memberDesc);
+
+ // Warning: Do not modify this array, or put it into unwrappingHintsByParamCount by reference, as the arrays
+ // inside that are modified!
+ final Class[] prepedParamTypes = preprocessParameterTypes(memberDesc);
+ final int paramCount = prepedParamTypes.length; // Must be the same as the length of the original param list
+
+ // Merge these unwrapping hints with the existing table of hints:
+ if (unwrappingHintsByParamCount == null) {
+ unwrappingHintsByParamCount = new Class[paramCount + 1][];
+ unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone();
+ } else if (unwrappingHintsByParamCount.length <= paramCount) {
+ Class[][] newUnwrappingHintsByParamCount = new Class[paramCount + 1][];
+ System.arraycopy(unwrappingHintsByParamCount, 0, newUnwrappingHintsByParamCount, 0,
+ unwrappingHintsByParamCount.length);
+ unwrappingHintsByParamCount = newUnwrappingHintsByParamCount;
+ unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone();
+ } else {
+ Class[] unwrappingHints = unwrappingHintsByParamCount[paramCount];
+ if (unwrappingHints == null) {
+ unwrappingHintsByParamCount[paramCount] = prepedParamTypes.clone();
+ } else {
+ for (int paramIdx = 0; paramIdx < prepedParamTypes.length; paramIdx++) {
+ // For each parameter list length, we merge the argument type arrays into a single Class[] that
+ // stores the most specific common types for each position. Hence we will possibly use a too generic
+ // hint for the unwrapping. For correct behavior, for each overloaded methods its own parameter
+ // types should be used as a hint. But without unwrapping the arguments, we couldn't select the
+ // overloaded method. So we had to unwrap with all possible target types of each parameter position,
+ // which would be slow and its result would be uncacheable (as we don't have anything usable as
+ // a lookup key). So we just use this compromise.
+ unwrappingHints[paramIdx] = getCommonSupertypeForUnwrappingHint(
+ unwrappingHints[paramIdx], prepedParamTypes[paramIdx]);
+ }
+ }
+ }
+
+ int[] typeFlagsByParamIdx = ALL_ZEROS_ARRAY;
+ // Fill typeFlagsByParamCount (if necessary)
+ for (int paramIdx = 0; paramIdx < paramCount; paramIdx++) {
+ final int typeFlags = TypeFlags.classToTypeFlags(prepedParamTypes[paramIdx]);
+ if (typeFlags != 0) {
+ if (typeFlagsByParamIdx == ALL_ZEROS_ARRAY) {
+ typeFlagsByParamIdx = new int[paramCount];
+ }
+ typeFlagsByParamIdx[paramIdx] = typeFlags;
+ }
+ }
+ mergeInTypesFlags(paramCount, typeFlagsByParamIdx);
+
+ afterWideningUnwrappingHints(prepedParamTypes, typeFlagsByParamIdx);
+ }
+
+ Class[][] getUnwrappingHintsByParamCount() {
+ return unwrappingHintsByParamCount;
+ }
+
+ @SuppressFBWarnings(value="JLM_JSR166_UTILCONCURRENT_MONITORENTER",
+ justification="Locks for member descriptor creation only")
+ final MaybeEmptyCallableMemberDescriptor getMemberDescriptorForArgs(Object[] args, boolean varArg) {
+ ArgumentTypes argTypes = new ArgumentTypes(args);
+ MaybeEmptyCallableMemberDescriptor memberDesc
+ = (MaybeEmptyCallableMemberDescriptor) argTypesToMemberDescCache.get(argTypes);
+ if (memberDesc == null) {
+ // Synchronized so that we won't unnecessarily create the same member desc. for multiple times in parallel.
+ synchronized (argTypesToMemberDescCache) {
+ memberDesc = (MaybeEmptyCallableMemberDescriptor) argTypesToMemberDescCache.get(argTypes);
+ if (memberDesc == null) {
+ memberDesc = argTypes.getMostSpecific(memberDescs, varArg);
+ argTypesToMemberDescCache.put(argTypes, memberDesc);
+ }
+ }
+ }
+ return memberDesc;
+ }
+
+ Iterator/*<ReflectionCallableMemberDescriptor>*/ getMemberDescriptors() {
+ return memberDescs.iterator();
+ }
+
+ abstract Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc);
+ abstract void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes);
+
+ abstract MaybeEmptyMemberAndArguments getMemberAndArguments(List/*<TemplateModel>*/ tmArgs,
+ DefaultObjectWrapper unwrapper) throws TemplateModelException;
+
+ /**
+ * Returns the most specific common class (or interface) of two parameter types for the purpose of unwrapping.
+ * This is trickier than finding the most specific overlapping superclass of two classes, because:
+ * <ul>
+ * <li>It considers primitive classes as the subclasses of the boxing classes.</li>
+ * <li>If the only common class is {@link Object}, it will try to find a common interface. If there are more
+ * of them, it will start removing those that are known to be uninteresting as unwrapping hints.</li>
+ * </ul>
+ *
+ * @param c1 Parameter type 1
+ * @param c2 Parameter type 2
+ */
+ protected Class getCommonSupertypeForUnwrappingHint(Class c1, Class c2) {
+ if (c1 == c2) return c1;
+ // This also means that the hint for (Integer, Integer) will be Integer, not just Number. This is consistent
+ // with how non-overloaded method hints work.
+
+ // c1 primitive class to boxing class:
+ final boolean c1WasPrim;
+ if (c1.isPrimitive()) {
+ c1 = _ClassUtil.primitiveClassToBoxingClass(c1);
+ c1WasPrim = true;
+ } else {
+ c1WasPrim = false;
+ }
+
+ // c2 primitive class to boxing class:
+ final boolean c2WasPrim;
+ if (c2.isPrimitive()) {
+ c2 = _ClassUtil.primitiveClassToBoxingClass(c2);
+ c2WasPrim = true;
+ } else {
+ c2WasPrim = false;
+ }
+
+ if (c1 == c2) {
+ // If it was like int and Integer, boolean and Boolean, etc., we return the boxing type (as that's the
+ // less specific, because it allows null.)
+ // (If it was two equivalent primitives, we don't get here, because of the 1st line of the method.)
+ return c1;
+ } else if (Number.class.isAssignableFrom(c1) && Number.class.isAssignableFrom(c2)) {
+ // We don't want the unwrapper to convert to a numerical super-type [*] as it's not yet known what the
+ // actual number type of the chosen method will be. We will postpone the actual numerical conversion
+ // until that, especially as some conversions (like oms point to floating point) can be lossy.
+ // * Numerical super-type: Like long > int > short > byte.
+ return Number.class;
+ } else if (c1WasPrim || c2WasPrim) {
+ // At this point these all stand:
+ // - At least one of them was primitive
+ // - No more than one of them was numerical
+ // - They don't have the same wrapper (boxing) class
+ return Object.class;
+ }
+
+ // We never get to this point if buxfixed is true and any of these stands:
+ // - One of classes was a primitive type
+ // - One of classes was a numerical type (either boxing type or primitive)
+
+ Set commonTypes = _MethodUtil.getAssignables(c1, c2);
+ commonTypes.retainAll(_MethodUtil.getAssignables(c2, c1));
+ if (commonTypes.isEmpty()) {
+ // Can happen when at least one of the arguments is an interface, as
+ // they don't have Object at the root of their hierarchy
+ return Object.class;
+ }
+
+ // Gather maximally specific elements. Yes, there can be more than one
+ // because of interfaces. I.e., if you call this method for String.class
+ // and Number.class, you'll have Comparable, Serializable, and Object as
+ // maximal elements.
+ List max = new ArrayList();
+ listCommonTypes: for (Iterator commonTypesIter = commonTypes.iterator(); commonTypesIter.hasNext(); ) {
+ Class clazz = (Class) commonTypesIter.next();
+ for (Iterator maxIter = max.iterator(); maxIter.hasNext(); ) {
+ Class maxClazz = (Class) maxIter.next();
+ if (_MethodUtil.isMoreOrSameSpecificParameterType(maxClazz, clazz, false /*bugfixed [1]*/, 0) != 0) {
+ // clazz can't be maximal, if there's already a more specific or equal maximal than it.
+ continue listCommonTypes;
+ }
+ if (_MethodUtil.isMoreOrSameSpecificParameterType(clazz, maxClazz, false /*bugfixed [1]*/, 0) != 0) {
+ // If it's more specific than a currently maximal element,
+ // that currently maximal is no longer a maximal.
+ maxIter.remove();
+ }
+ // 1: We don't use bugfixed at the "[1]"-marked points because it's slower and doesn't make any
+ // difference here as it's ensured that nor c1 nor c2 is primitive or numerical. The bugfix has only
+ // affected the treatment of primitives and numerical types.
+ }
+ // If we get here, no current maximal is more specific than the
+ // current class, so clazz is a new maximal so far.
+ max.add(clazz);
+ }
+
+ if (max.size() > 1) { // we have an ambiguity
+ // Find the non-interface class
+ for (Iterator it = max.iterator(); it.hasNext(); ) {
+ Class maxCl = (Class) it.next();
+ if (!maxCl.isInterface()) {
+ if (maxCl != Object.class) { // This actually shouldn't ever happen, but to be sure...
+ // If it's not Object, we use it as the most specific
+ return maxCl;
+ } else {
+ // Otherwise remove Object, and we will try with the interfaces
+ it.remove();
+ }
+ }
+ }
+
+ // At this point we only have interfaces left.
+ // Try removing interfaces about which we know that they are useless as unwrapping hints:
+ max.remove(Cloneable.class);
+ if (max.size() > 1) { // Still have an ambiguity...
+ max.remove(Serializable.class);
+ if (max.size() > 1) { // Still had an ambiguity...
+ max.remove(Comparable.class);
+ if (max.size() > 1) {
+ return Object.class; // Still had an ambiguity... no luck.
+ }
+ }
+ }
+ }
+
+ return (Class) max.get(0);
+ }
+
+ /**
+ * Gets the "type flags" of each parameter positions, or {@code null} if there's no method with this parameter
+ * count or if we are in pre-2.3.21 mode, or {@link #ALL_ZEROS_ARRAY} if there were no parameters that turned
+ * on a flag. The returned {@code int}-s are one or more {@link TypeFlags} constants binary "or"-ed together.
+ */
+ final protected int[] getTypeFlags(int paramCount) {
+ return typeFlagsByParamCount != null && typeFlagsByParamCount.length > paramCount
+ ? typeFlagsByParamCount[paramCount]
+ : null;
+ }
+
+ /**
+ * Updates the content of the {@link #typeFlagsByParamCount} field with the parameter type flags of a method.
+ *
+ * @param dstParamCount The parameter count for which we want to merge in the type flags
+ * @param srcTypeFlagsByParamIdx If shorter than {@code dstParamCount}, its last item will be repeated until
+ * dstParamCount length is reached. If longer, the excessive items will be ignored.
+ * Maybe {@link #ALL_ZEROS_ARRAY}. Maybe a 0-length array. Can't be {@code null}.
+ */
+ final protected void mergeInTypesFlags(int dstParamCount, int[] srcTypeFlagsByParamIdx) {
+ _NullArgumentException.check("srcTypesFlagsByParamIdx", srcTypeFlagsByParamIdx);
+
+ // Special case of 0 param count:
+ if (dstParamCount == 0) {
+ if (typeFlagsByParamCount == null) {
+ typeFlagsByParamCount = ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY;
+ } else if (typeFlagsByParamCount != ZERO_PARAM_COUNT_TYPE_FLAGS_ARRAY) {
+ typeFlagsByParamCount[0] = ALL_ZEROS_ARRAY;
+ }
+ return;
+ }
+
+ // Ensure that typesFlagsByParamCount[dstParamCount] exists:
+ if (typeFlagsByParamCount == null) {
+ typeFlagsByParamCount = new int[dstParamCount + 1][];
+ } else if (typeFlagsByParamCount.length <= dstParamCount) {
+ int[][] newTypeFlagsByParamCount = new int[dstParamCount + 1][];
+ System.arraycopy(typeFlagsByParamCount, 0, newTypeFlagsByParamCount, 0,
+ typeFlagsByParamCount.length);
+ typeFlagsByParamCount = newTypeFlagsByParamCount;
+ }
+
+ int[] dstTypeFlagsByParamIdx = typeFlagsByParamCount[dstParamCount];
+ if (dstTypeFlagsByParamIdx == null) {
+ // This is the first method added with this number of params => no merging
+
+ if (srcTypeFlagsByParamIdx != ALL_ZEROS_ARRAY) {
+ int srcParamCount = srcTypeFlagsByParamIdx.length;
+ dstTypeFlagsByParamIdx = new int[dstParamCount];
+ for (int paramIdx = 0; paramIdx < dstParamCount; paramIdx++) {
+ dstTypeFlagsByParamIdx[paramIdx]
+ = srcTypeFlagsByParamIdx[paramIdx < srcParamCount ? paramIdx : srcParamCount - 1];
+ }
+ } else {
+ dstTypeFlagsByParamIdx = ALL_ZEROS_ARRAY;
+ }
+
+ typeFlagsByParamCount[dstParamCount] = dstTypeFlagsByParamIdx;
+ } else {
+ // dstTypeFlagsByParamIdx != null, so we need to merge into it.
+
+ if (srcTypeFlagsByParamIdx == dstTypeFlagsByParamIdx) {
+ // Used to occur when both are ALL_ZEROS_ARRAY
+ return;
+ }
+
+ // As we will write dstTypeFlagsByParamIdx, it can't remain ALL_ZEROS_ARRAY anymore.
+ if (dstTypeFlagsByParamIdx == ALL_ZEROS_ARRAY && dstParamCount > 0) {
+ dstTypeFlagsByParamIdx = new int[dstParamCount];
+ typeFlagsByParamCount[dstParamCount] = dstTypeFlagsByParamIdx;
+ }
+
+ for (int paramIdx = 0; paramIdx < dstParamCount; paramIdx++) {
+ final int srcParamTypeFlags;
+ if (srcTypeFlagsByParamIdx != ALL_ZEROS_ARRAY) {
+ int srcParamCount = srcTypeFlagsByParamIdx.length;
+ srcParamTypeFlags = srcTypeFlagsByParamIdx[paramIdx < srcParamCount ? paramIdx : srcParamCount - 1];
+ } else {
+ srcParamTypeFlags = 0;
+ }
+
+ final int dstParamTypesFlags = dstTypeFlagsByParamIdx[paramIdx];
+ if (dstParamTypesFlags != srcParamTypeFlags) {
+ int mergedTypeFlags = dstParamTypesFlags | srcParamTypeFlags;
+ if ((mergedTypeFlags & TypeFlags.MASK_ALL_NUMERICALS) != 0) {
+ // Must not be set if we don't have numerical type at this index!
+ mergedTypeFlags |= TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT;
+ }
+ dstTypeFlagsByParamIdx[paramIdx] = mergedTypeFlags;
+ }
+ }
+ }
+ }
+
+ protected void forceNumberArgumentsToParameterTypes(
+ Object[] args, Class[] paramTypes, int[] typeFlagsByParamIndex) {
+ final int paramTypesLen = paramTypes.length;
+ final int argsLen = args.length;
+ for (int argIdx = 0; argIdx < argsLen; argIdx++) {
+ final int paramTypeIdx = argIdx < paramTypesLen ? argIdx : paramTypesLen - 1;
+ final int typeFlags = typeFlagsByParamIndex[paramTypeIdx];
+
+ // Forcing the number type can only be interesting if there are numerical parameter types on that index,
+ // and the unwrapping was not to an exact numerical type.
+ if ((typeFlags & TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT) != 0) {
+ final Object arg = args[argIdx];
+ // If arg isn't a number, we can't do any conversions anyway, regardless of the param type.
+ if (arg instanceof Number) {
+ final Class targetType = paramTypes[paramTypeIdx];
+ final Number convertedArg = DefaultObjectWrapper.forceUnwrappedNumberToType((Number) arg, targetType);
+ if (convertedArg != null) {
+ args[argIdx] = convertedArg;
+ }
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtil.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtil.java b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtil.java
new file mode 100644
index 0000000..0b05a6a
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtil.java
@@ -0,0 +1,1289 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.apache.freemarker.core.model.TemplateNumberModel;
+import org.apache.freemarker.core.util._ClassUtil;
+import org.apache.freemarker.core.util._NumberUtil;
+
+/**
+ * Everything related to coercion to ambiguous numerical types.
+ */
+class OverloadedNumberUtil {
+
+ // Can't be instantiated
+ private OverloadedNumberUtil() { }
+
+ /**
+ * The lower limit of conversion prices where there's a risk of significant mantissa loss.
+ * The value comes from misc/overloadedNumberRules/prices.ods and generator.ftl.
+ */
+ static final int BIG_MANTISSA_LOSS_PRICE = 4 * 10000;
+
+ /** The highest long that can be stored in double without precision loss: 2**53. */
+ private static final long MAX_DOUBLE_OR_LONG = 9007199254740992L;
+ /** The lowest long that can be stored in double without precision loss: -(2**53). */
+ private static final long MIN_DOUBLE_OR_LONG = -9007199254740992L;
+ private static final int MAX_DOUBLE_OR_LONG_LOG_2 = 53;
+
+ /** The highest long that can be stored in float without precision loss: 2**24. */
+ private static final int MAX_FLOAT_OR_INT = 16777216;
+ /** The lowest long that can be stored in float without precision loss: -(2**24). */
+ private static final int MIN_FLOAT_OR_INT = -16777216;
+ private static final int MAX_FLOAT_OR_INT_LOG_2 = 24;
+ /** Lowest number that we don't thread as possible integer 0. */
+ private static final double LOWEST_ABOVE_ZERO = 0.000001;
+ /** Highest number that we don't thread as possible integer 1. */
+ private static final double HIGHEST_BELOW_ONE = 0.999999;
+
+ /**
+ * Attaches the lowest alternative number type to the parameter number via {@link NumberWithFallbackType}, if
+ * that's useful according the possible target number types. This transformation is applied on the method call
+ * argument list before overloaded method selection.
+ *
+ * <p>Note that as of this writing, this method is only used when
+ * {@link DefaultObjectWrapper#getIncompatibleImprovements()} >= 2.3.21.
+ *
+ * <p>Why's this needed, how it works: Overloaded method selection only selects methods where the <em>type</em>
+ * (not the value!) of the argument is "smaller" or the same as the parameter type. This is similar to how it's in
+ * the Java language. That it only decides based on the parameter type is important because this way
+ * {@link OverloadedMethodsSubset} can cache method lookup decisions using the types as the cache key. Problem is,
+ * since you don't declare the exact numerical types in FTL, and FTL has only a single generic numeric type
+ * anyway, what Java type a {@link TemplateNumberModel} uses internally is often seen as a technical detail of which
+ * the template author can't always keep track of. So we investigate the <em>value</em> of the number too,
+ * then coerce it down without overflow to a type that will match the most overloaded methods. (This
+ * is especially important as FTL often stores numbers in {@link BigDecimal}-s, which will hardly ever match any
+ * method parameters.) We could simply return that number, like {@code Byte(0)} for an {@code Integer(0)},
+ * however, then we would lose the information about what the original type was. The original type is sometimes
+ * important, as in ambiguous situations the method where there's an exact type match should be selected (like,
+ * when someone wants to select an overload explicitly with {@code m(x?int)}). Also, if an overload wins where
+ * the parameter type at the position of the number is {@code Number} or {@code Object} (or {@code Comparable}
+ * etc.), it's expected that we pass in the original value (an {@code Integer} in this example), especially if that
+ * value is the return value of another Java method. That's why we use
+ * {@link NumberWithFallbackType} numerical classes like {@link IntegerOrByte}, which represents both the original
+ * type and the coerced type, all encoded into the class of the value, which is used as the overloaded method lookup
+ * cache key.
+ *
+ * <p>See also: <tt>src\main\misc\overloadedNumberRules\prices.ods</tt>.
+ *
+ * @param num the number to coerce
+ * @param typeFlags the type flags of the target parameter position; see {@link TypeFlags}
+ *
+ * @returns The original number or a {@link NumberWithFallbackType}, depending on the actual value and the types
+ * indicated in the {@code targetNumTypes} parameter.
+ */
+ static Number addFallbackType(final Number num, final int typeFlags) {
+ final Class numClass = num.getClass();
+ if (numClass == BigDecimal.class) {
+ // For now we only support the backward-compatible mode that doesn't prevent roll overs and magnitude loss.
+ // However, we push the overloaded selection to the right direction, so we will at least indicate if the
+ // number has decimals.
+ BigDecimal n = (BigDecimal) num;
+ if ((typeFlags & TypeFlags.MASK_KNOWN_INTEGERS) != 0
+ && (typeFlags & TypeFlags.MASK_KNOWN_NONINTEGERS) != 0
+ && _NumberUtil.isIntegerBigDecimal(n) /* <- can be expensive */) {
+ return new IntegerBigDecimal(n);
+ } else {
+ // Either it was a non-integer, or it didn't mater what it was, as we don't have both integer and
+ // non-integer target types.
+ return n;
+ }
+ } else if (numClass == Integer.class) {
+ int pn = num.intValue();
+ // Note that we try to return the most specific type (i.e., the numerical type with the smallest range), but
+ // only among the types that are possible targets. Like if the only target is int and the value is 1, we
+ // will return Integer 1, not Byte 1, even though byte is automatically converted to int so it would
+ // work too. Why we avoid unnecessarily specific types is that they generate more overloaded method lookup
+ // cache entries, since the cache key is the array of the types of the argument values. So we want as few
+ // permutations as possible.
+ if ((typeFlags & TypeFlags.BYTE) != 0 && pn <= Byte.MAX_VALUE && pn >= Byte.MIN_VALUE) {
+ return new IntegerOrByte((Integer) num, (byte) pn);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0 && pn <= Short.MAX_VALUE && pn >= Short.MIN_VALUE) {
+ return new IntegerOrShort((Integer) num, (short) pn);
+ } else {
+ return num;
+ }
+ } else if (numClass == Long.class) {
+ final long pn = num.longValue();
+ if ((typeFlags & TypeFlags.BYTE) != 0 && pn <= Byte.MAX_VALUE && pn >= Byte.MIN_VALUE) {
+ return new LongOrByte((Long) num, (byte) pn);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0 && pn <= Short.MAX_VALUE && pn >= Short.MIN_VALUE) {
+ return new LongOrShort((Long) num, (short) pn);
+ } else if ((typeFlags & TypeFlags.INTEGER) != 0 && pn <= Integer.MAX_VALUE && pn >= Integer.MIN_VALUE) {
+ return new LongOrInteger((Long) num, (int) pn);
+ } else {
+ return num;
+ }
+ } else if (numClass == Double.class) {
+ final double doubleN = num.doubleValue();
+
+ // Can we store it in an integer type?
+ checkIfWholeNumber: do {
+ if ((typeFlags & TypeFlags.MASK_KNOWN_INTEGERS) == 0) break checkIfWholeNumber;
+
+ // There's no hope to be 1-precise outside this region. (Although problems can occur even inside it...)
+ if (doubleN > MAX_DOUBLE_OR_LONG || doubleN < MIN_DOUBLE_OR_LONG) break checkIfWholeNumber;
+
+ long longN = num.longValue();
+ double diff = doubleN - longN;
+ boolean exact; // We will try to ignore precision glitches (like 0.3 - 0.2 - 0.1 = -2.7E-17)
+ if (diff == 0) {
+ exact = true;
+ } else if (diff > 0) {
+ if (diff < LOWEST_ABOVE_ZERO) {
+ exact = false;
+ } else if (diff > HIGHEST_BELOW_ONE) {
+ exact = false;
+ longN++;
+ } else {
+ break checkIfWholeNumber;
+ }
+ } else { // => diff < 0
+ if (diff > -LOWEST_ABOVE_ZERO) {
+ exact = false;
+ } else if (diff < -HIGHEST_BELOW_ONE) {
+ exact = false;
+ longN--;
+ } else {
+ break checkIfWholeNumber;
+ }
+ }
+
+ // If we reach this, it can be treated as a whole number.
+
+ if ((typeFlags & TypeFlags.BYTE) != 0
+ && longN <= Byte.MAX_VALUE && longN >= Byte.MIN_VALUE) {
+ return new DoubleOrByte((Double) num, (byte) longN);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0
+ && longN <= Short.MAX_VALUE && longN >= Short.MIN_VALUE) {
+ return new DoubleOrShort((Double) num, (short) longN);
+ } else if ((typeFlags & TypeFlags.INTEGER) != 0
+ && longN <= Integer.MAX_VALUE && longN >= Integer.MIN_VALUE) {
+ final int intN = (int) longN;
+ return (typeFlags & TypeFlags.FLOAT) != 0 && intN >= MIN_FLOAT_OR_INT && intN <= MAX_FLOAT_OR_INT
+ ? new DoubleOrIntegerOrFloat((Double) num, intN)
+ : new DoubleOrInteger((Double) num, intN);
+ } else if ((typeFlags & TypeFlags.LONG) != 0) {
+ if (exact) {
+ return new DoubleOrLong((Double) num, longN);
+ } else {
+ // We don't deal with non-exact numbers outside the range of int, as we already reach
+ // ULP 2.384185791015625E-7 there.
+ if (longN >= Integer.MIN_VALUE && longN <= Integer.MAX_VALUE) {
+ return new DoubleOrLong((Double) num, longN);
+ } else {
+ break checkIfWholeNumber;
+ }
+ }
+ }
+ // This point is reached if the double value was out of the range of target integer type(s).
+ // Falls through!
+ } while (false);
+ // If we reach this that means that it can't be treated as a whole number.
+
+ if ((typeFlags & TypeFlags.FLOAT) != 0 && doubleN >= -Float.MAX_VALUE && doubleN <= Float.MAX_VALUE) {
+ return new DoubleOrFloat((Double) num);
+ } else {
+ // Simply Double:
+ return num;
+ }
+ } else if (numClass == Float.class) {
+ final float floatN = num.floatValue();
+
+ // Can we store it in an integer type?
+ checkIfWholeNumber: do {
+ if ((typeFlags & TypeFlags.MASK_KNOWN_INTEGERS) == 0) break checkIfWholeNumber;
+
+ // There's no hope to be 1-precise outside this region. (Although problems can occur even inside it...)
+ if (floatN > MAX_FLOAT_OR_INT || floatN < MIN_FLOAT_OR_INT) break checkIfWholeNumber;
+
+ int intN = num.intValue();
+ double diff = floatN - intN;
+ boolean exact; // We will try to ignore precision glitches (like 0.3 - 0.2 - 0.1 = -2.7E-17)
+ if (diff == 0) {
+ exact = true;
+ // We already reach ULP 7.6293945E-6 with bytes, so we don't continue with shorts.
+ } else if (intN >= Byte.MIN_VALUE && intN <= Byte.MAX_VALUE) {
+ if (diff > 0) {
+ if (diff < 0.00001) {
+ exact = false;
+ } else if (diff > 0.99999) {
+ exact = false;
+ intN++;
+ } else {
+ break checkIfWholeNumber;
+ }
+ } else { // => diff < 0
+ if (diff > -0.00001) {
+ exact = false;
+ } else if (diff < -0.99999) {
+ exact = false;
+ intN--;
+ } else {
+ break checkIfWholeNumber;
+ }
+ }
+ } else {
+ break checkIfWholeNumber;
+ }
+
+ // If we reach this, it can be treated as a whole number.
+
+ if ((typeFlags & TypeFlags.BYTE) != 0 && intN <= Byte.MAX_VALUE && intN >= Byte.MIN_VALUE) {
+ return new FloatOrByte((Float) num, (byte) intN);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0 && intN <= Short.MAX_VALUE && intN >= Short.MIN_VALUE) {
+ return new FloatOrShort((Float) num, (short) intN);
+ } else if ((typeFlags & TypeFlags.INTEGER) != 0) {
+ return new FloatOrInteger((Float) num, intN);
+ } else if ((typeFlags & TypeFlags.LONG) != 0) {
+ // We can't even go outside the range of integers, so we don't need Long variation:
+ return exact
+ ? new FloatOrInteger((Float) num, intN)
+ : new FloatOrByte((Float) num, (byte) intN); // as !exact implies (-128..127)
+ }
+ // This point is reached if the float value was out of the range of target integer type(s).
+ // Falls through!
+ } while (false);
+ // If we reach this that means that it can't be treated as a whole number. So it's simply a Float:
+ return num;
+ } else if (numClass == Byte.class) {
+ return num;
+ } else if (numClass == Short.class) {
+ short pn = num.shortValue();
+ if ((typeFlags & TypeFlags.BYTE) != 0 && pn <= Byte.MAX_VALUE && pn >= Byte.MIN_VALUE) {
+ return new ShortOrByte((Short) num, (byte) pn);
+ } else {
+ return num;
+ }
+ } else if (numClass == BigInteger.class) {
+ if ((typeFlags
+ & ((TypeFlags.MASK_KNOWN_INTEGERS | TypeFlags.MASK_KNOWN_NONINTEGERS)
+ ^ (TypeFlags.BIG_INTEGER | TypeFlags.BIG_DECIMAL))) != 0) {
+ BigInteger biNum = (BigInteger) num;
+ final int bitLength = biNum.bitLength(); // Doesn't include sign bit, so it's one less than expected
+ if ((typeFlags & TypeFlags.BYTE) != 0 && bitLength <= 7) {
+ return new BigIntegerOrByte(biNum);
+ } else if ((typeFlags & TypeFlags.SHORT) != 0 && bitLength <= 15) {
+ return new BigIntegerOrShort(biNum);
+ } else if ((typeFlags & TypeFlags.INTEGER) != 0 && bitLength <= 31) {
+ return new BigIntegerOrInteger(biNum);
+ } else if ((typeFlags & TypeFlags.LONG) != 0 && bitLength <= 63) {
+ return new BigIntegerOrLong(biNum);
+ } else if ((typeFlags & TypeFlags.FLOAT) != 0
+ && (bitLength <= MAX_FLOAT_OR_INT_LOG_2
+ || bitLength == MAX_FLOAT_OR_INT_LOG_2 + 1
+ && biNum.getLowestSetBit() >= MAX_FLOAT_OR_INT_LOG_2)) {
+ return new BigIntegerOrFloat(biNum);
+ } else if ((typeFlags & TypeFlags.DOUBLE) != 0
+ && (bitLength <= MAX_DOUBLE_OR_LONG_LOG_2
+ || bitLength == MAX_DOUBLE_OR_LONG_LOG_2 + 1
+ && biNum.getLowestSetBit() >= MAX_DOUBLE_OR_LONG_LOG_2)) {
+ return new BigIntegerOrDouble(biNum);
+ } else {
+ return num;
+ }
+ } else {
+ // No relevant coercion target types; return the BigInteger as is:
+ return num;
+ }
+ } else {
+ // Unknown number type:
+ return num;
+ }
+ }
+
+ interface ByteSource { Byte byteValue(); }
+ interface ShortSource { Short shortValue(); }
+ interface IntegerSource { Integer integerValue(); }
+ interface LongSource { Long longValue(); }
+ interface FloatSource { Float floatValue(); }
+ interface DoubleSource { Double doubleValue(); }
+ interface BigIntegerSource { BigInteger bigIntegerValue(); }
+ interface BigDecimalSource { BigDecimal bigDecimalValue(); }
+
+ /**
+ * Superclass of "Or"-ed numerical types. With an example, a {@code int} 1 has the fallback type {@code byte}, as
+ * that's the smallest type that can store the value, so it can be represented as an {@link IntegerOrByte}.
+ * This is useful as overloaded method selection only examines the type of the arguments, not the value of them,
+ * but with "Or"-ed types we can encode this value-related information into the argument type, hence influencing the
+ * method selection.
+ */
+ abstract static class NumberWithFallbackType extends Number implements Comparable {
+
+ protected abstract Number getSourceNumber();
+
+ @Override
+ public int intValue() {
+ return getSourceNumber().intValue();
+ }
+
+ @Override
+ public long longValue() {
+ return getSourceNumber().longValue();
+ }
+
+ @Override
+ public float floatValue() {
+ return getSourceNumber().floatValue();
+ }
+
+ @Override
+ public double doubleValue() {
+ return getSourceNumber().doubleValue();
+ }
+
+ @Override
+ public byte byteValue() {
+ return getSourceNumber().byteValue();
+ }
+
+ @Override
+ public short shortValue() {
+ return getSourceNumber().shortValue();
+ }
+
+ @Override
+ public int hashCode() {
+ return getSourceNumber().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj != null && getClass() == obj.getClass()) {
+ return getSourceNumber().equals(((NumberWithFallbackType) obj).getSourceNumber());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getSourceNumber().toString();
+ }
+
+ // We have to implement this, so that if a potential matching method expects a Comparable, which is implemented
+ // by all the supported numerical types, the "Or" type will be a match.
+ @Override
+ public int compareTo(Object o) {
+ Number n = getSourceNumber();
+ if (n instanceof Comparable) {
+ return ((Comparable) n).compareTo(o);
+ } else {
+ throw new ClassCastException(n.getClass().getName() + " is not Comparable.");
+ }
+ }
+
+ }
+
+ /**
+ * Holds a {@link BigDecimal} that stores a whole number. When selecting a overloaded method, FreeMarker tries to
+ * associate {@link BigDecimal} values to parameters of types that can hold non-whole numbers, unless the
+ * {@link BigDecimal} is wrapped into this class, in which case it does the opposite. This mechanism is, however,
+ * too rough to prevent roll overs or magnitude losses. Those are not yet handled for backward compatibility (they
+ * were suppressed earlier too).
+ */
+ static final class IntegerBigDecimal extends NumberWithFallbackType {
+
+ private final BigDecimal n;
+
+ IntegerBigDecimal(BigDecimal n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ public BigInteger bigIntegerValue() {
+ return n.toBigInteger();
+ }
+
+ }
+
+ static abstract class LongOrSmallerInteger extends NumberWithFallbackType {
+
+ private final Long n;
+
+ protected LongOrSmallerInteger(Long n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public long longValue() {
+ return n.longValue();
+ }
+
+ }
+
+ static class LongOrByte extends LongOrSmallerInteger {
+
+ private final byte w;
+
+ LongOrByte(Long n, byte w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ }
+
+ static class LongOrShort extends LongOrSmallerInteger {
+
+ private final short w;
+
+ LongOrShort(Long n, short w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ }
+
+ static class LongOrInteger extends LongOrSmallerInteger {
+
+ private final int w;
+
+ LongOrInteger(Long n, int w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ }
+
+ static abstract class IntegerOrSmallerInteger extends NumberWithFallbackType {
+
+ private final Integer n;
+
+ protected IntegerOrSmallerInteger(Integer n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public int intValue() {
+ return n.intValue();
+ }
+
+ }
+
+ static class IntegerOrByte extends IntegerOrSmallerInteger {
+
+ private final byte w;
+
+ IntegerOrByte(Integer n, byte w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ }
+
+ static class IntegerOrShort extends IntegerOrSmallerInteger {
+
+ private final short w;
+
+ IntegerOrShort(Integer n, short w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ }
+
+ static class ShortOrByte extends NumberWithFallbackType {
+
+ private final Short n;
+ private final byte w;
+
+ protected ShortOrByte(Short n, byte w) {
+ this.n = n;
+ this.w = w;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public short shortValue() {
+ return n.shortValue();
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ }
+
+ static abstract class DoubleOrWholeNumber extends NumberWithFallbackType {
+
+ private final Double n;
+
+ protected DoubleOrWholeNumber(Double n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public double doubleValue() {
+ return n.doubleValue();
+ }
+
+ }
+
+ static final class DoubleOrByte extends DoubleOrWholeNumber {
+
+ private final byte w;
+
+ DoubleOrByte(Double n, byte w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrShort extends DoubleOrWholeNumber {
+
+ private final short w;
+
+ DoubleOrShort(Double n, short w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrIntegerOrFloat extends DoubleOrWholeNumber {
+
+ private final int w;
+
+ DoubleOrIntegerOrFloat(Double n, int w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrInteger extends DoubleOrWholeNumber {
+
+ private final int w;
+
+ DoubleOrInteger(Double n, int w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrLong extends DoubleOrWholeNumber {
+
+ private final long w;
+
+ DoubleOrLong(Double n, long w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class DoubleOrFloat extends NumberWithFallbackType {
+
+ private final Double n;
+
+ DoubleOrFloat(Double n) {
+ this.n = n;
+ }
+
+ @Override
+ public float floatValue() {
+ return n.floatValue();
+ }
+
+ @Override
+ public double doubleValue() {
+ return n.doubleValue();
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ }
+
+ static abstract class FloatOrWholeNumber extends NumberWithFallbackType {
+
+ private final Float n;
+
+ FloatOrWholeNumber(Float n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ @Override
+ public float floatValue() {
+ return n.floatValue();
+ }
+
+ }
+
+ static final class FloatOrByte extends FloatOrWholeNumber {
+
+ private final byte w;
+
+ FloatOrByte(Float n, byte w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public byte byteValue() {
+ return w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class FloatOrShort extends FloatOrWholeNumber {
+
+ private final short w;
+
+ FloatOrShort(Float n, short w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public short shortValue() {
+ return w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ static final class FloatOrInteger extends FloatOrWholeNumber {
+
+ private final int w;
+
+ FloatOrInteger(Float n, int w) {
+ super(n);
+ this.w = w;
+ }
+
+ @Override
+ public int intValue() {
+ return w;
+ }
+
+ @Override
+ public long longValue() {
+ return w;
+ }
+
+ }
+
+ abstract static class BigIntegerOrPrimitive extends NumberWithFallbackType {
+
+ protected final BigInteger n;
+
+ BigIntegerOrPrimitive(BigInteger n) {
+ this.n = n;
+ }
+
+ @Override
+ protected Number getSourceNumber() {
+ return n;
+ }
+
+ }
+
+ final static class BigIntegerOrByte extends BigIntegerOrPrimitive {
+
+ BigIntegerOrByte(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ final static class BigIntegerOrShort extends BigIntegerOrPrimitive {
+
+ BigIntegerOrShort(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ final static class BigIntegerOrInteger extends BigIntegerOrPrimitive {
+
+ BigIntegerOrInteger(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ final static class BigIntegerOrLong extends BigIntegerOrPrimitive {
+
+ BigIntegerOrLong(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ abstract static class BigIntegerOrFPPrimitive extends BigIntegerOrPrimitive {
+
+ BigIntegerOrFPPrimitive(BigInteger n) {
+ super(n);
+ }
+
+ /** Faster version of {@link BigDecimal#floatValue()}, utilizes that the number known to fit into a long. */
+ @Override
+ public float floatValue() {
+ return n.longValue();
+ }
+
+ /** Faster version of {@link BigDecimal#doubleValue()}, utilizes that the number known to fit into a long. */
+ @Override
+ public double doubleValue() {
+ return n.longValue();
+ }
+
+ }
+
+ final static class BigIntegerOrFloat extends BigIntegerOrFPPrimitive {
+
+ BigIntegerOrFloat(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ final static class BigIntegerOrDouble extends BigIntegerOrFPPrimitive {
+
+ BigIntegerOrDouble(BigInteger n) {
+ super(n);
+ }
+
+ }
+
+ /**
+ * Returns a non-negative number that indicates how much we want to avoid a given numerical type conversion. Since
+ * we only consider the types here, not the actual value, we always consider the worst case scenario. Like it will
+ * say that converting int to short is not allowed, although int 1 can be converted to byte without loss. To account
+ * for such situations, "Or"-ed types, like {@link IntegerOrByte} has to be used.
+ *
+ * @param fromC the non-primitive type of the argument (with other words, the actual type).
+ * Must be {@link Number} or its subclass. This is possibly an {@link NumberWithFallbackType} subclass.
+ * @param toC the <em>non-primitive</em> type of the target parameter (with other words, the format type).
+ * Must be a {@link Number} subclass, not {@link Number} itself.
+ * Must <em>not</em> be {@link NumberWithFallbackType} or its subclass.
+ *
+ * @return
+ * <p>The possible values are:
+ * <ul>
+ * <li>0: No conversion is needed
+ * <li>[0, 30000): Lossless conversion
+ * <li>[30000, 40000): Smaller precision loss in mantissa is possible.
+ * <li>[40000, 50000): Bigger precision loss in mantissa is possible.
+ * <li>{@link Integer#MAX_VALUE}: Conversion not allowed due to the possibility of magnitude loss or
+ * overflow</li>
+ * </ul>
+ *
+ * <p>At some places, we only care if the conversion is possible, i.e., whether the return value is
+ * {@link Integer#MAX_VALUE} or not. But when multiple overloaded methods have an argument type to which we
+ * could convert to, this number will influence which of those will be chosen.
+ */
+ static int getArgumentConversionPrice(Class fromC, Class toC) {
+ // DO NOT EDIT, generated code!
+ // See: src\main\misc\overloadedNumberRules\README.txt
+ if (toC == fromC) {
+ return 0;
+ } else if (toC == Integer.class) {
+ if (fromC == IntegerBigDecimal.class) return 31003;
+ else if (fromC == BigDecimal.class) return 41003;
+ else if (fromC == Long.class) return Integer.MAX_VALUE;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 10003;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return 21003;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 22003;
+ else if (fromC == DoubleOrInteger.class) return 22003;
+ else if (fromC == DoubleOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerOrByte.class) return 0;
+ else if (fromC == DoubleOrByte.class) return 22003;
+ else if (fromC == LongOrByte.class) return 21003;
+ else if (fromC == Short.class) return 10003;
+ else if (fromC == LongOrShort.class) return 21003;
+ else if (fromC == ShortOrByte.class) return 10003;
+ else if (fromC == FloatOrInteger.class) return 21003;
+ else if (fromC == FloatOrByte.class) return 21003;
+ else if (fromC == FloatOrShort.class) return 21003;
+ else if (fromC == BigIntegerOrInteger.class) return 16003;
+ else if (fromC == BigIntegerOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrByte.class) return 16003;
+ else if (fromC == IntegerOrShort.class) return 0;
+ else if (fromC == DoubleOrShort.class) return 22003;
+ else if (fromC == BigIntegerOrShort.class) return 16003;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Long.class) {
+ if (fromC == Integer.class) return 10004;
+ else if (fromC == IntegerBigDecimal.class) return 31004;
+ else if (fromC == BigDecimal.class) return 41004;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 10004;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return 0;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 21004;
+ else if (fromC == DoubleOrInteger.class) return 21004;
+ else if (fromC == DoubleOrLong.class) return 21004;
+ else if (fromC == IntegerOrByte.class) return 10004;
+ else if (fromC == DoubleOrByte.class) return 21004;
+ else if (fromC == LongOrByte.class) return 0;
+ else if (fromC == Short.class) return 10004;
+ else if (fromC == LongOrShort.class) return 0;
+ else if (fromC == ShortOrByte.class) return 10004;
+ else if (fromC == FloatOrInteger.class) return 21004;
+ else if (fromC == FloatOrByte.class) return 21004;
+ else if (fromC == FloatOrShort.class) return 21004;
+ else if (fromC == BigIntegerOrInteger.class) return 15004;
+ else if (fromC == BigIntegerOrLong.class) return 15004;
+ else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrByte.class) return 15004;
+ else if (fromC == IntegerOrShort.class) return 10004;
+ else if (fromC == DoubleOrShort.class) return 21004;
+ else if (fromC == BigIntegerOrShort.class) return 15004;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Double.class) {
+ if (fromC == Integer.class) return 20007;
+ else if (fromC == IntegerBigDecimal.class) return 32007;
+ else if (fromC == BigDecimal.class) return 32007;
+ else if (fromC == Long.class) return 30007;
+ else if (fromC == Float.class) return 10007;
+ else if (fromC == Byte.class) return 20007;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return 21007;
+ else if (fromC == DoubleOrFloat.class) return 0;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 0;
+ else if (fromC == DoubleOrInteger.class) return 0;
+ else if (fromC == DoubleOrLong.class) return 0;
+ else if (fromC == IntegerOrByte.class) return 20007;
+ else if (fromC == DoubleOrByte.class) return 0;
+ else if (fromC == LongOrByte.class) return 21007;
+ else if (fromC == Short.class) return 20007;
+ else if (fromC == LongOrShort.class) return 21007;
+ else if (fromC == ShortOrByte.class) return 20007;
+ else if (fromC == FloatOrInteger.class) return 10007;
+ else if (fromC == FloatOrByte.class) return 10007;
+ else if (fromC == FloatOrShort.class) return 10007;
+ else if (fromC == BigIntegerOrInteger.class) return 20007;
+ else if (fromC == BigIntegerOrLong.class) return 30007;
+ else if (fromC == BigIntegerOrDouble.class) return 20007;
+ else if (fromC == BigIntegerOrFloat.class) return 20007;
+ else if (fromC == BigIntegerOrByte.class) return 20007;
+ else if (fromC == IntegerOrShort.class) return 20007;
+ else if (fromC == DoubleOrShort.class) return 0;
+ else if (fromC == BigIntegerOrShort.class) return 20007;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Float.class) {
+ if (fromC == Integer.class) return 30006;
+ else if (fromC == IntegerBigDecimal.class) return 33006;
+ else if (fromC == BigDecimal.class) return 33006;
+ else if (fromC == Long.class) return 40006;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 20006;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return 30006;
+ else if (fromC == DoubleOrFloat.class) return 30006;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 23006;
+ else if (fromC == DoubleOrInteger.class) return 30006;
+ else if (fromC == DoubleOrLong.class) return 40006;
+ else if (fromC == IntegerOrByte.class) return 24006;
+ else if (fromC == DoubleOrByte.class) return 23006;
+ else if (fromC == LongOrByte.class) return 24006;
+ else if (fromC == Short.class) return 20006;
+ else if (fromC == LongOrShort.class) return 24006;
+ else if (fromC == ShortOrByte.class) return 20006;
+ else if (fromC == FloatOrInteger.class) return 0;
+ else if (fromC == FloatOrByte.class) return 0;
+ else if (fromC == FloatOrShort.class) return 0;
+ else if (fromC == BigIntegerOrInteger.class) return 30006;
+ else if (fromC == BigIntegerOrLong.class) return 40006;
+ else if (fromC == BigIntegerOrDouble.class) return 40006;
+ else if (fromC == BigIntegerOrFloat.class) return 24006;
+ else if (fromC == BigIntegerOrByte.class) return 24006;
+ else if (fromC == IntegerOrShort.class) return 24006;
+ else if (fromC == DoubleOrShort.class) return 23006;
+ else if (fromC == BigIntegerOrShort.class) return 24006;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Byte.class) {
+ if (fromC == Integer.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerBigDecimal.class) return 35001;
+ else if (fromC == BigDecimal.class) return 45001;
+ else if (fromC == Long.class) return Integer.MAX_VALUE;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerOrByte.class) return 22001;
+ else if (fromC == DoubleOrByte.class) return 25001;
+ else if (fromC == LongOrByte.class) return 23001;
+ else if (fromC == Short.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrShort.class) return Integer.MAX_VALUE;
+ else if (fromC == ShortOrByte.class) return 21001;
+ else if (fromC == FloatOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == FloatOrByte.class) return 23001;
+ else if (fromC == FloatOrShort.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrByte.class) return 18001;
+ else if (fromC == IntegerOrShort.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrShort.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrShort.class) return Integer.MAX_VALUE;
+ else return Integer.MAX_VALUE;
+ } else if (toC == Short.class) {
+ if (fromC == Integer.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerBigDecimal.class) return 34002;
+ else if (fromC == BigDecimal.class) return 44002;
+ else if (fromC == Long.class) return Integer.MAX_VALUE;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 10002;
+ else if (fromC == BigInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == LongOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == IntegerOrByte.class) return 21002;
+ else if (fromC == DoubleOrByte.class) return 24002;
+ else if (fromC == LongOrByte.class) return 22002;
+ else if (fromC == LongOrShort.class) return 22002;
+ else if (fromC == ShortOrByte.class) return 0;
+ else if (fromC == FloatOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == FloatOrByte.class) return 22002;
+ else if (fromC == FloatOrShort.class) return 22002;
+ else if (fromC == BigIntegerOrInteger.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrLong.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrDouble.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == BigIntegerOrByte.class) return 17002;
+ else if (fromC == IntegerOrShort.class) return 21002;
+ else if (fromC == DoubleOrShort.class) return 24002;
+ else if (fromC == BigIntegerOrShort.class) return 17002;
+ else return Integer.MAX_VALUE;
+ } else if (toC == BigDecimal.class) {
+ if (fromC == Integer.class) return 20008;
+ else if (fromC == IntegerBigDecimal.class) return 0;
+ else if (fromC == Long.class) return 20008;
+ else if (fromC == Double.class) return 20008;
+ else if (fromC == Float.class) return 20008;
+ else if (fromC == Byte.class) return 20008;
+ else if (fromC == BigInteger.class) return 10008;
+ else if (fromC == LongOrInteger.class) return 20008;
+ else if (fromC == DoubleOrFloat.class) return 20008;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 20008;
+ else if (fromC == DoubleOrInteger.class) return 20008;
+ else if (fromC == DoubleOrLong.class) return 20008;
+ else if (fromC == IntegerOrByte.class) return 20008;
+ else if (fromC == DoubleOrByte.class) return 20008;
+ else if (fromC == LongOrByte.class) return 20008;
+ else if (fromC == Short.class) return 20008;
+ else if (fromC == LongOrShort.class) return 20008;
+ else if (fromC == ShortOrByte.class) return 20008;
+ else if (fromC == FloatOrInteger.class) return 20008;
+ else if (fromC == FloatOrByte.class) return 20008;
+ else if (fromC == FloatOrShort.class) return 20008;
+ else if (fromC == BigIntegerOrInteger.class) return 10008;
+ else if (fromC == BigIntegerOrLong.class) return 10008;
+ else if (fromC == BigIntegerOrDouble.class) return 10008;
+ else if (fromC == BigIntegerOrFloat.class) return 10008;
+ else if (fromC == BigIntegerOrByte.class) return 10008;
+ else if (fromC == IntegerOrShort.class) return 20008;
+ else if (fromC == DoubleOrShort.class) return 20008;
+ else if (fromC == BigIntegerOrShort.class) return 10008;
+ else return Integer.MAX_VALUE;
+ } else if (toC == BigInteger.class) {
+ if (fromC == Integer.class) return 10005;
+ else if (fromC == IntegerBigDecimal.class) return 10005;
+ else if (fromC == BigDecimal.class) return 40005;
+ else if (fromC == Long.class) return 10005;
+ else if (fromC == Double.class) return Integer.MAX_VALUE;
+ else if (fromC == Float.class) return Integer.MAX_VALUE;
+ else if (fromC == Byte.class) return 10005;
+ else if (fromC == LongOrInteger.class) return 10005;
+ else if (fromC == DoubleOrFloat.class) return Integer.MAX_VALUE;
+ else if (fromC == DoubleOrIntegerOrFloat.class) return 21005;
+ else if (fromC == DoubleOrInteger.class) return 21005;
+ else if (fromC == DoubleOrLong.class) return 21005;
+ else if (fromC == IntegerOrByte.class) return 10005;
+ else if (fromC == DoubleOrByte.class) return 21005;
+ else if (fromC == LongOrByte.class) return 10005;
+ else if (fromC == Short.class) return 10005;
+ else if (fromC == LongOrShort.class) return 10005;
+ else if (fromC == ShortOrByte.class) return 10005;
+ else if (fromC == FloatOrInteger.class) return 25005;
+ else if (fromC == FloatOrByte.class) return 25005;
+ else if (fromC == FloatOrShort.class) return 25005;
+ else if (fromC == BigIntegerOrInteger.class) return 0;
+ else if (fromC == BigIntegerOrLong.class) return 0;
+ else if (fromC == BigIntegerOrDouble.class) return 0;
+ else if (fromC == BigIntegerOrFloat.class) return 0;
+ else if (fromC == BigIntegerOrByte.class) return 0;
+ else if (fromC == IntegerOrShort.class) return 10005;
+ else if (fromC == DoubleOrShort.class) return 21005;
+ else if (fromC == BigIntegerOrShort.class) return 0;
+ else return Integer.MAX_VALUE;
+ } else {
+ // Unknown toC; we don't know how to convert to it:
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ static int compareNumberTypeSpecificity(Class c1, Class c2) {
+ // DO NOT EDIT, generated code!
+ // See: src\main\misc\overloadedNumberRules\README.txt
+ c1 = _ClassUtil.primitiveClassToBoxingClass(c1);
+ c2 = _ClassUtil.primitiveClassToBoxingClass(c2);
+
+ if (c1 == c2) return 0;
+
+ if (c1 == Integer.class) {
+ if (c2 == Long.class) return 4 - 3;
+ if (c2 == Double.class) return 7 - 3;
+ if (c2 == Float.class) return 6 - 3;
+ if (c2 == Byte.class) return 1 - 3;
+ if (c2 == Short.class) return 2 - 3;
+ if (c2 == BigDecimal.class) return 8 - 3;
+ if (c2 == BigInteger.class) return 5 - 3;
+ return 0;
+ }
+ if (c1 == Long.class) {
+ if (c2 == Integer.class) return 3 - 4;
+ if (c2 == Double.class) return 7 - 4;
+ if (c2 == Float.class) return 6 - 4;
+ if (c2 == Byte.class) return 1 - 4;
+ if (c2 == Short.class) return 2 - 4;
+ if (c2 == BigDecimal.class) return 8 - 4;
+ if (c2 == BigInteger.class) return 5 - 4;
+ return 0;
+ }
+ if (c1 == Double.class) {
+ if (c2 == Integer.class) return 3 - 7;
+ if (c2 == Long.class) return 4 - 7;
+ if (c2 == Float.class) return 6 - 7;
+ if (c2 == Byte.class) return 1 - 7;
+ if (c2 == Short.class) return 2 - 7;
+ if (c2 == BigDecimal.class) return 8 - 7;
+ if (c2 == BigInteger.class) return 5 - 7;
+ return 0;
+ }
+ if (c1 == Float.class) {
+ if (c2 == Integer.class) return 3 - 6;
+ if (c2 == Long.class) return 4 - 6;
+ if (c2 == Double.class) return 7 - 6;
+ if (c2 == Byte.class) return 1 - 6;
+ if (c2 == Short.class) return 2 - 6;
+ if (c2 == BigDecimal.class) return 8 - 6;
+ if (c2 == BigInteger.class) return 5 - 6;
+ return 0;
+ }
+ if (c1 == Byte.class) {
+ if (c2 == Integer.class) return 3 - 1;
+ if (c2 == Long.class) return 4 - 1;
+ if (c2 == Double.class) return 7 - 1;
+ if (c2 == Float.class) return 6 - 1;
+ if (c2 == Short.class) return 2 - 1;
+ if (c2 == BigDecimal.class) return 8 - 1;
+ if (c2 == BigInteger.class) return 5 - 1;
+ return 0;
+ }
+ if (c1 == Short.class) {
+ if (c2 == Integer.class) return 3 - 2;
+ if (c2 == Long.class) return 4 - 2;
+ if (c2 == Double.class) return 7 - 2;
+ if (c2 == Float.class) return 6 - 2;
+ if (c2 == Byte.class) return 1 - 2;
+ if (c2 == BigDecimal.class) return 8 - 2;
+ if (c2 == BigInteger.class) return 5 - 2;
+ return 0;
+ }
+ if (c1 == BigDecimal.class) {
+ if (c2 == Integer.class) return 3 - 8;
+ if (c2 == Long.class) return 4 - 8;
+ if (c2 == Double.class) return 7 - 8;
+ if (c2 == Float.class) return 6 - 8;
+ if (c2 == Byte.class) return 1 - 8;
+ if (c2 == Short.class) return 2 - 8;
+ if (c2 == BigInteger.class) return 5 - 8;
+ return 0;
+ }
+ if (c1 == BigInteger.class) {
+ if (c2 == Integer.class) return 3 - 5;
+ if (c2 == Long.class) return 4 - 5;
+ if (c2 == Double.class) return 7 - 5;
+ if (c2 == Float.class) return 6 - 5;
+ if (c2 == Byte.class) return 1 - 5;
+ if (c2 == Short.class) return 2 - 5;
+ if (c2 == BigDecimal.class) return 8 - 5;
+ return 0;
+ }
+ return 0;
+ }
+
+}
[12/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperConfiguration.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperConfiguration.java
deleted file mode 100644
index 94997cb..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BeansWrapperConfiguration.java
+++ /dev/null
@@ -1,232 +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 org.apache.freemarker.core.Version;
-import org.apache.freemarker.core._CoreAPI;
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
-
-/**
- * Holds {@link BeansWrapper} configuration settings and defines their defaults.
- * You will not use this abstract class directly, but concrete subclasses like {@link BeansWrapperBuilder} and
- * {@link DefaultObjectWrapperBuilder}. Unless, you are developing a builder for a custom {@link BeansWrapper} subclass.
- *
- * <p>This class is designed so that its instances can be used as lookup keys in a singleton cache. This is also why
- * this class defines the configuration setting defaults for {@link BeansWrapper}, instead of leaving that to
- * {@link BeansWrapper} itself. (Because, the default values influence the lookup key, and the singleton needs to be
- * looked up without creating a {@link BeansWrapper} instance.) However, because instances are mutable, you should
- * deep-clone it with {@link #clone(boolean)} before using it as cache key.
- *
- * @since 2.3.21
- */
-public abstract class BeansWrapperConfiguration implements Cloneable {
-
- private final Version incompatibleImprovements;
-
- protected ClassIntrospectorBuilder classIntrospectorFactory;
-
- // Properties and their *defaults*:
- private boolean simpleMapWrapper = false;
- private int defaultDateType = TemplateDateModel.UNKNOWN;
- private ObjectWrapper outerIdentity = null;
- private boolean strict = false;
- private boolean useModelCache = false;
- // Attention!
- // - As this object is a cache key, non-normalized field values should be avoided.
- // - Fields with default values must be set until the end of the constructor to ensure that when the lookup happens,
- // there will be no unset fields.
- // - If you add a new field, review all methods in this class
-
- /**
- * @param incompatibleImprovements
- * See the corresponding parameter of {@link BeansWrapper#BeansWrapper(Version)}. Not {@code null}. Note
- * that the version will be normalized to the lowest version where the same incompatible
- * {@link BeansWrapper} improvements were already present, so for the returned instance
- * {@link #getIncompatibleImprovements()} might returns a lower version than what you have specified
- * here.
- * @param isIncompImprsAlreadyNormalized
- * Tells if the {@code incompatibleImprovements} parameter contains an <em>already normalized</em> value.
- * This parameter meant to be {@code true} when the class that extends {@link BeansWrapper} needs to add
- * additional breaking versions over those of {@link BeansWrapper}. Thus, if this parameter is
- * {@code true}, the versions where {@link BeansWrapper} had breaking changes must be already factored
- * into the {@code incompatibleImprovements} parameter value, as no more normalization will happen. (You
- * can use {@link BeansWrapper#normalizeIncompatibleImprovementsVersion(Version)} to discover those.)
- *
- * @since 2.3.22
- */
- protected BeansWrapperConfiguration(Version incompatibleImprovements, boolean isIncompImprsAlreadyNormalized) {
- _CoreAPI.checkVersionNotNullAndSupported(incompatibleImprovements);
-
- incompatibleImprovements = isIncompImprsAlreadyNormalized
- ? incompatibleImprovements
- : BeansWrapper.normalizeIncompatibleImprovementsVersion(incompatibleImprovements);
- this.incompatibleImprovements = incompatibleImprovements;
-
- classIntrospectorFactory = new ClassIntrospectorBuilder(incompatibleImprovements);
- }
-
- /**
- * Same as {@link #BeansWrapperConfiguration(Version, boolean) BeansWrapperConfiguration(Version, false)}.
- */
- protected BeansWrapperConfiguration(Version incompatibleImprovements) {
- this(incompatibleImprovements, false);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + incompatibleImprovements.hashCode();
- result = prime * result + (simpleMapWrapper ? 1231 : 1237);
- result = prime * result + defaultDateType;
- result = prime * result + (outerIdentity != null ? outerIdentity.hashCode() : 0);
- result = prime * result + (strict ? 1231 : 1237);
- result = prime * result + (useModelCache ? 1231 : 1237);
- result = prime * result + classIntrospectorFactory.hashCode();
- return result;
- }
-
- /**
- * Two {@link BeansWrapperConfiguration}-s are equal exactly if their classes are identical ({@code ==}), and their
- * field values are equal.
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
- BeansWrapperConfiguration other = (BeansWrapperConfiguration) obj;
-
- if (!incompatibleImprovements.equals(other.incompatibleImprovements)) return false;
- if (simpleMapWrapper != other.simpleMapWrapper) return false;
- if (defaultDateType != other.defaultDateType) return false;
- if (outerIdentity != other.outerIdentity) return false;
- if (strict != other.strict) return false;
- if (useModelCache != other.useModelCache) return false;
- return classIntrospectorFactory.equals(other.classIntrospectorFactory);
- }
-
- protected Object clone(boolean deepCloneKey) {
- try {
- BeansWrapperConfiguration clone = (BeansWrapperConfiguration) super.clone();
- if (deepCloneKey) {
- clone.classIntrospectorFactory
- = (ClassIntrospectorBuilder) classIntrospectorFactory.clone();
- }
- return clone;
- } catch (CloneNotSupportedException e) {
- throw new RuntimeException("Failed to clone BeansWrapperConfiguration", e);
- }
- }
-
- public boolean isSimpleMapWrapper() {
- return simpleMapWrapper;
- }
-
- /** See {@link BeansWrapper#setSimpleMapWrapper(boolean)}. */
- public void setSimpleMapWrapper(boolean simpleMapWrapper) {
- this.simpleMapWrapper = simpleMapWrapper;
- }
-
- public int getDefaultDateType() {
- return defaultDateType;
- }
-
- /** See {@link BeansWrapper#setDefaultDateType(int)}. */
- public void setDefaultDateType(int defaultDateType) {
- this.defaultDateType = defaultDateType;
- }
-
- public ObjectWrapper getOuterIdentity() {
- return outerIdentity;
- }
-
- /**
- * See {@link BeansWrapper#setOuterIdentity(ObjectWrapper)}, except here the default is {@code null} that means
- * the {@link ObjectWrapper} that you will set up with this {@link BeansWrapperBuilder} object.
- */
- public void setOuterIdentity(ObjectWrapper outerIdentity) {
- this.outerIdentity = outerIdentity;
- }
-
- public boolean isStrict() {
- return strict;
- }
-
- /** See {@link BeansWrapper#setStrict(boolean)}. */
- public void setStrict(boolean strict) {
- this.strict = strict;
- }
-
- public boolean getUseModelCache() {
- return useModelCache;
- }
-
- /** See {@link BeansWrapper#setUseModelCache(boolean)} (it means the same). */
- public void setUseModelCache(boolean useModelCache) {
- this.useModelCache = useModelCache;
- }
-
- public Version getIncompatibleImprovements() {
- return incompatibleImprovements;
- }
-
- public int getExposureLevel() {
- return classIntrospectorFactory.getExposureLevel();
- }
-
- /** See {@link BeansWrapper#setExposureLevel(int)}. */
- public void setExposureLevel(int exposureLevel) {
- classIntrospectorFactory.setExposureLevel(exposureLevel);
- }
-
- public boolean getExposeFields() {
- return classIntrospectorFactory.getExposeFields();
- }
-
- /** See {@link BeansWrapper#setExposeFields(boolean)}. */
- public void setExposeFields(boolean exposeFields) {
- classIntrospectorFactory.setExposeFields(exposeFields);
- }
-
- public MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
- return classIntrospectorFactory.getMethodAppearanceFineTuner();
- }
-
- /**
- * See {@link BeansWrapper#setMethodAppearanceFineTuner(MethodAppearanceFineTuner)}; additionally,
- * note that currently setting this to non-{@code null} will disable class introspection cache sharing, unless
- * the value implements {@link SingletonCustomizer}.
- */
- public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner methodAppearanceFineTuner) {
- classIntrospectorFactory.setMethodAppearanceFineTuner(methodAppearanceFineTuner);
- }
-
- MethodSorter getMethodSorter() {
- return classIntrospectorFactory.getMethodSorter();
- }
-
- void setMethodSorter(MethodSorter methodSorter) {
- classIntrospectorFactory.setMethodSorter(methodSorter);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/BooleanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/BooleanModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/BooleanModel.java
deleted file mode 100644
index 857df36..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/BooleanModel.java
+++ /dev/null
@@ -1,40 +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 org.apache.freemarker.core.model.TemplateBooleanModel;
-
-/**
- * <p>A class that will wrap instances of {@link java.lang.Boolean} into a
- * {@link TemplateBooleanModel}.
- */
-public class BooleanModel extends BeanModel implements TemplateBooleanModel {
- private final boolean value;
-
- public BooleanModel(Boolean bool, BeansWrapper wrapper) {
- super(bool, wrapper, false);
- value = bool.booleanValue();
- }
-
- @Override
- public boolean getAsBoolean() {
- return value;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/CallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/CallableMemberDescriptor.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/CallableMemberDescriptor.java
deleted file mode 100644
index fb55571..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/CallableMemberDescriptor.java
+++ /dev/null
@@ -1,56 +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.Method;
-
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * Packs a {@link Method} or {@link Constructor} together with its parameter types. The actual
- * {@link Method} or {@link Constructor} is not exposed by the API, because in rare cases calling them require
- * type conversion that the Java reflection API can't do, hence the developer shouldn't be tempted to call them
- * directly.
- */
-abstract class CallableMemberDescriptor extends MaybeEmptyCallableMemberDescriptor {
-
- abstract TemplateModel invokeMethod(BeansWrapper bw, Object obj, Object[] args)
- throws TemplateModelException, InvocationTargetException, IllegalAccessException;
-
- abstract Object invokeConstructor(BeansWrapper bw, Object[] args)
- throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException,
- TemplateModelException;
-
- abstract String getDeclaration();
-
- abstract boolean isConstructor();
-
- abstract boolean isStatic();
-
- abstract boolean isVarargs();
-
- abstract Class[] getParamTypes();
-
- abstract String getName();
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/CharacterOrString.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/CharacterOrString.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/CharacterOrString.java
deleted file mode 100644
index 4bd297b..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/CharacterOrString.java
+++ /dev/null
@@ -1,45 +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 org.apache.freemarker.core.model.TemplateScalarModel;
-
-/**
- * Represents value unwrapped both to {@link Character} and {@link String}. This is needed for unwrapped overloaded
- * method parameters where both {@link Character} and {@link String} occurs on the same parameter position when the
- * {@link TemplateScalarModel} to unwrapp contains a {@link String} of length 1.
- */
-final class CharacterOrString {
-
- private final String stringValue;
-
- CharacterOrString(String stringValue) {
- this.stringValue = stringValue;
- }
-
- String getAsString() {
- return stringValue;
- }
-
- char getAsChar() {
- return stringValue.charAt(0);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassBasedModelFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassBasedModelFactory.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassBasedModelFactory.java
deleted file mode 100644
index 1fd4d61..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassBasedModelFactory.java
+++ /dev/null
@@ -1,148 +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.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.util._ClassUtil;
-
-/**
- * Base class for hash models keyed by Java class names.
- */
-abstract class ClassBasedModelFactory implements TemplateHashModel {
- private final BeansWrapper wrapper;
-
- private final Map/*<String,TemplateModel>*/ cache = new ConcurrentHashMap();
- private final Set classIntrospectionsInProgress = new HashSet();
-
- protected ClassBasedModelFactory(BeansWrapper wrapper) {
- this.wrapper = wrapper;
- }
-
- @Override
- public TemplateModel get(String key) throws TemplateModelException {
- try {
- return getInternal(key);
- } catch (Exception e) {
- if (e instanceof TemplateModelException) {
- throw (TemplateModelException) e;
- } else {
- throw new TemplateModelException(e);
- }
- }
- }
-
- private TemplateModel getInternal(String key) throws TemplateModelException, ClassNotFoundException {
- {
- TemplateModel model = (TemplateModel) cache.get(key);
- if (model != null) return model;
- }
-
- final ClassIntrospector classIntrospector;
- int classIntrospectorClearingCounter;
- final Object sharedLock = wrapper.getSharedIntrospectionLock();
- synchronized (sharedLock) {
- TemplateModel model = (TemplateModel) cache.get(key);
- if (model != null) return model;
-
- while (model == null
- && classIntrospectionsInProgress.contains(key)) {
- // Another thread is already introspecting this class;
- // waiting for its result.
- try {
- sharedLock.wait();
- model = (TemplateModel) cache.get(key);
- } catch (InterruptedException e) {
- throw new RuntimeException(
- "Class inrospection data lookup aborded: " + e);
- }
- }
- if (model != null) return model;
-
- // This will be the thread that introspects this class.
- classIntrospectionsInProgress.add(key);
-
- // While the classIntrospector should not be changed from another thread, badly written apps can do that,
- // and it's cheap to get the classIntrospector from inside the lock here:
- classIntrospector = wrapper.getClassIntrospector();
- classIntrospectorClearingCounter = classIntrospector.getClearingCounter();
- }
- try {
- final Class clazz = _ClassUtil.forName(key);
-
- // This is called so that we trigger the
- // class-reloading detector. If clazz is a reloaded class,
- // the wrapper will in turn call our clearCache method.
- // TODO: Why do we check it now and only now?
- classIntrospector.get(clazz);
-
- TemplateModel model = createModel(clazz);
- // Warning: model will be null if the class is not good for the subclass.
- // For example, EnumModels#createModel returns null if clazz is not an enum.
-
- if (model != null) {
- synchronized (sharedLock) {
- // Save it into the cache, but only if nothing relevant has changed while we were outside the lock:
- if (classIntrospector == wrapper.getClassIntrospector()
- && classIntrospectorClearingCounter == classIntrospector.getClearingCounter()) {
- cache.put(key, model);
- }
- }
- }
- return model;
- } finally {
- synchronized (sharedLock) {
- classIntrospectionsInProgress.remove(key);
- sharedLock.notifyAll();
- }
- }
- }
-
- void clearCache() {
- synchronized (wrapper.getSharedIntrospectionLock()) {
- cache.clear();
- }
- }
-
- void removeFromCache(Class clazz) {
- synchronized (wrapper.getSharedIntrospectionLock()) {
- cache.remove(clazz.getName());
- }
- }
-
- @Override
- public boolean isEmpty() {
- return false;
- }
-
- protected abstract TemplateModel createModel(Class clazz)
- throws TemplateModelException;
-
- protected BeansWrapper getWrapper() {
- return wrapper;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassChangeNotifier.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassChangeNotifier.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassChangeNotifier.java
deleted file mode 100644
index f85fafd..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassChangeNotifier.java
+++ /dev/null
@@ -1,32 +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;
-
-/**
- * Reports when the non-private interface of a class was changed to the subscribers.
- */
-interface ClassChangeNotifier {
-
- /**
- * @param classIntrospector Should only be weak-referenced from the monitor object.
- */
- void subscribe(ClassIntrospector classIntrospector);
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospector.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospector.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospector.java
deleted file mode 100644
index d1cf191..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospector.java
+++ /dev/null
@@ -1,808 +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.beans.BeanInfo;
-import java.beans.IndexedPropertyDescriptor;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.MethodDescriptor;
-import java.beans.PropertyDescriptor;
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecision;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecisionInput;
-import org.apache.freemarker.core.util.BugException;
-import org.apache.freemarker.core.util._NullArgumentException;
-import org.slf4j.Logger;
-
-/**
- * Returns information about a {@link Class} that's useful for FreeMarker. Encapsulates a cache for this. Thread-safe,
- * doesn't even require "proper publishing" starting from 2.3.24 or Java 5. Immutable, with the exception of the
- * internal caches.
- *
- * <p>
- * Note that instances of this are cached on the level of FreeMarker's defining class loader. Hence, it must not do
- * operations that depend on the Thread Context Class Loader, such as resolving class names.
- */
-class ClassIntrospector {
-
- // Attention: This class must be thread-safe (not just after proper publishing). This is important as some of
- // these are shared by many object wrappers, and concurrency related glitches due to user errors must remain
- // local to the object wrappers, not corrupting the shared ClassIntrospector.
-
- private static final Logger LOG = _CoreLogs.BEANS_WRAPPER;
-
- private static final String JREBEL_SDK_CLASS_NAME = "org.zeroturnaround.javarebel.ClassEventListener";
- private static final String JREBEL_INTEGRATION_ERROR_MSG
- = "Error initializing JRebel integration. JRebel integration disabled.";
-
- private static final ClassChangeNotifier CLASS_CHANGE_NOTIFIER;
- static {
- boolean jRebelAvailable;
- try {
- Class.forName(JREBEL_SDK_CLASS_NAME);
- jRebelAvailable = true;
- } catch (Throwable e) {
- jRebelAvailable = false;
- try {
- if (!(e instanceof ClassNotFoundException)) {
- LOG.error(JREBEL_INTEGRATION_ERROR_MSG, e);
- }
- } catch (Throwable loggingE) {
- // ignore
- }
- }
-
- ClassChangeNotifier classChangeNotifier;
- if (jRebelAvailable) {
- try {
- classChangeNotifier = (ClassChangeNotifier)
- Class.forName("org.apache.freemarker.core.model.impl.beans.JRebelClassChangeNotifier").newInstance();
- } catch (Throwable e) {
- classChangeNotifier = null;
- try {
- LOG.error(JREBEL_INTEGRATION_ERROR_MSG, e);
- } catch (Throwable loggingE) {
- // ignore
- }
- }
- } else {
- classChangeNotifier = null;
- }
-
- CLASS_CHANGE_NOTIFIER = classChangeNotifier;
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Introspection info Map keys:
-
- /** Key in the class info Map to the Map that maps method to argument type arrays */
- private static final Object ARG_TYPES_BY_METHOD_KEY = new Object();
- /** Key in the class info Map to the object that represents the constructors (one or multiple due to overloading) */
- static final Object CONSTRUCTORS_KEY = new Object();
- /** Key in the class info Map to the get(String|Object) Method */
- static final Object GENERIC_GET_KEY = new Object();
-
- // -----------------------------------------------------------------------------------------------------------------
- // Introspection configuration properties:
-
- // Note: These all must be *declared* final (or else synchronization is needed everywhere where they are accessed).
-
- final int exposureLevel;
- final boolean exposeFields;
- final MethodAppearanceFineTuner methodAppearanceFineTuner;
- final MethodSorter methodSorter;
-
- /** See {@link #getHasSharedInstanceRestrictons()} */
- final private boolean hasSharedInstanceRestrictons;
-
- /** See {@link #isShared()} */
- final private boolean shared;
-
- // -----------------------------------------------------------------------------------------------------------------
- // State fields:
-
- private final Object sharedLock;
- private final Map<Class<?>, Map<Object, Object>> cache
- = new ConcurrentHashMap<>(0, 0.75f, 16);
- private final Set<String> cacheClassNames = new HashSet<>(0);
- private final Set<Class<?>> classIntrospectionsInProgress = new HashSet<>(0);
-
- private final List<WeakReference<Object/*ClassBasedModelFactory|ModelCache>*/>> modelFactories
- = new LinkedList<>();
- private final ReferenceQueue<Object> modelFactoriesRefQueue = new ReferenceQueue<>();
-
- private int clearingCounter;
-
- // -----------------------------------------------------------------------------------------------------------------
- // Instantiation:
-
- /**
- * Creates a new instance, that is hence surely not shared (singleton) instance.
- *
- * @param pa
- * Stores what the values of the JavaBean properties of the returned instance will be. Not {@code null}.
- */
- ClassIntrospector(ClassIntrospectorBuilder pa, Object sharedLock) {
- this(pa, sharedLock, false, false);
- }
-
- /**
- * @param hasSharedInstanceRestrictons
- * {@code true} exactly if we are creating a new instance with {@link ClassIntrospectorBuilder}. Then
- * it's {@code true} even if it won't put the instance into the cache.
- */
- ClassIntrospector(ClassIntrospectorBuilder builder, Object sharedLock,
- boolean hasSharedInstanceRestrictons, boolean shared) {
- _NullArgumentException.check("sharedLock", sharedLock);
-
- exposureLevel = builder.getExposureLevel();
- exposeFields = builder.getExposeFields();
- methodAppearanceFineTuner = builder.getMethodAppearanceFineTuner();
- methodSorter = builder.getMethodSorter();
-
- this.sharedLock = sharedLock;
-
- this.hasSharedInstanceRestrictons = hasSharedInstanceRestrictons;
- this.shared = shared;
-
- if (CLASS_CHANGE_NOTIFIER != null) {
- CLASS_CHANGE_NOTIFIER.subscribe(this);
- }
- }
-
- /**
- * Returns a {@link ClassIntrospectorBuilder}-s that could be used to create an identical {@link #ClassIntrospector}
- * . The returned {@link ClassIntrospectorBuilder} can be modified without interfering with anything.
- */
- ClassIntrospectorBuilder getPropertyAssignments() {
- return new ClassIntrospectorBuilder(this);
- }
-
- // ------------------------------------------------------------------------------------------------------------------
- // Introspection:
-
- /**
- * Gets the class introspection data from {@link #cache}, automatically creating the cache entry if it's missing.
- *
- * @return A {@link Map} where each key is a property/method/field name (or a special {@link Object} key like
- * {@link #CONSTRUCTORS_KEY}), each value is a {@link PropertyDescriptor} or {@link Method} or
- * {@link OverloadedMethods} or {@link Field} (but better check the source code...).
- */
- Map<Object, Object> get(Class<?> clazz) {
- {
- Map<Object, Object> introspData = cache.get(clazz);
- if (introspData != null) return introspData;
- }
-
- String className;
- synchronized (sharedLock) {
- Map<Object, Object> introspData = cache.get(clazz);
- if (introspData != null) return introspData;
-
- className = clazz.getName();
- if (cacheClassNames.contains(className)) {
- onSameNameClassesDetected(className);
- }
-
- while (introspData == null && classIntrospectionsInProgress.contains(clazz)) {
- // Another thread is already introspecting this class;
- // waiting for its result.
- try {
- sharedLock.wait();
- introspData = cache.get(clazz);
- } catch (InterruptedException e) {
- throw new RuntimeException(
- "Class inrospection data lookup aborded: " + e);
- }
- }
- if (introspData != null) return introspData;
-
- // This will be the thread that introspects this class.
- classIntrospectionsInProgress.add(clazz);
- }
- try {
- Map<Object, Object> introspData = createClassIntrospectionData(clazz);
- synchronized (sharedLock) {
- cache.put(clazz, introspData);
- cacheClassNames.add(className);
- }
- return introspData;
- } finally {
- synchronized (sharedLock) {
- classIntrospectionsInProgress.remove(clazz);
- sharedLock.notifyAll();
- }
- }
- }
-
- /**
- * Creates a {@link Map} with the content as described for the return value of {@link #get(Class)}.
- */
- private Map<Object, Object> createClassIntrospectionData(Class<?> clazz) {
- final Map<Object, Object> introspData = new HashMap<>();
-
- if (exposeFields) {
- addFieldsToClassIntrospectionData(introspData, clazz);
- }
-
- final Map<MethodSignature, List<Method>> accessibleMethods = discoverAccessibleMethods(clazz);
-
- addGenericGetToClassIntrospectionData(introspData, accessibleMethods);
-
- if (exposureLevel != BeansWrapper.EXPOSE_NOTHING) {
- try {
- addBeanInfoToClassIntrospectionData(introspData, clazz, accessibleMethods);
- } catch (IntrospectionException e) {
- LOG.warn("Couldn't properly perform introspection for class {}", clazz.getName(), e);
- introspData.clear(); // FIXME NBC: Don't drop everything here.
- }
- }
-
- addConstructorsToClassIntrospectionData(introspData, clazz);
-
- if (introspData.size() > 1) {
- return introspData;
- } else if (introspData.size() == 0) {
- return Collections.emptyMap();
- } else { // map.size() == 1
- Entry<Object, Object> e = introspData.entrySet().iterator().next();
- return Collections.singletonMap(e.getKey(), e.getValue());
- }
- }
-
- private void addFieldsToClassIntrospectionData(Map<Object, Object> introspData, Class<?> clazz)
- throws SecurityException {
- for (Field field : clazz.getFields()) {
- if ((field.getModifiers() & Modifier.STATIC) == 0) {
- introspData.put(field.getName(), field);
- }
- }
- }
-
- private void addBeanInfoToClassIntrospectionData(
- Map<Object, Object> introspData, Class<?> clazz, Map<MethodSignature, List<Method>> accessibleMethods)
- throws IntrospectionException {
- BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
-
- PropertyDescriptor[] pda = beanInfo.getPropertyDescriptors();
- if (pda != null) {
- int pdaLength = pda.length;
- for (int i = pdaLength - 1; i >= 0; --i) {
- addPropertyDescriptorToClassIntrospectionData(
- introspData, pda[i], clazz,
- accessibleMethods);
- }
- }
-
- if (exposureLevel < BeansWrapper.EXPOSE_PROPERTIES_ONLY) {
- final MethodAppearanceDecision decision = new MethodAppearanceDecision();
- MethodAppearanceDecisionInput decisionInput = null;
- final MethodDescriptor[] mda = sortMethodDescriptors(beanInfo.getMethodDescriptors());
- if (mda != null) {
- int mdaLength = mda.length;
- for (int i = mdaLength - 1; i >= 0; --i) {
- final MethodDescriptor md = mda[i];
- final Method method = getMatchingAccessibleMethod(md.getMethod(), accessibleMethods);
- if (method != null && isAllowedToExpose(method)) {
- decision.setDefaults(method);
- if (methodAppearanceFineTuner != null) {
- if (decisionInput == null) {
- decisionInput = new MethodAppearanceDecisionInput();
- }
- decisionInput.setContainingClass(clazz);
- decisionInput.setMethod(method);
-
- methodAppearanceFineTuner.process(decisionInput, decision);
- }
-
- PropertyDescriptor propDesc = decision.getExposeAsProperty();
- if (propDesc != null && !(introspData.get(propDesc.getName()) instanceof PropertyDescriptor)) {
- addPropertyDescriptorToClassIntrospectionData(
- introspData, propDesc, clazz, accessibleMethods);
- }
-
- String methodKey = decision.getExposeMethodAs();
- if (methodKey != null) {
- Object previous = introspData.get(methodKey);
- if (previous instanceof Method) {
- // Overloaded method - replace Method with a OverloadedMethods
- OverloadedMethods overloadedMethods = new OverloadedMethods();
- overloadedMethods.addMethod((Method) previous);
- overloadedMethods.addMethod(method);
- introspData.put(methodKey, overloadedMethods);
- // Remove parameter type information
- getArgTypesByMethod(introspData).remove(previous);
- } else if (previous instanceof OverloadedMethods) {
- // Already overloaded method - add new overload
- ((OverloadedMethods) previous).addMethod(method);
- } else if (decision.getMethodShadowsProperty()
- || !(previous instanceof PropertyDescriptor)) {
- // Simple method (this far)
- introspData.put(methodKey, method);
- getArgTypesByMethod(introspData).put(method,
- method.getParameterTypes());
- }
- }
- }
- } // for each in mda
- } // if mda != null
- } // end if (exposureLevel < EXPOSE_PROPERTIES_ONLY)
- }
-
- private void addPropertyDescriptorToClassIntrospectionData(Map<Object, Object> introspData,
- PropertyDescriptor pd, Class<?> clazz, Map<MethodSignature, List<Method>> accessibleMethods) {
- if (pd instanceof IndexedPropertyDescriptor) {
- IndexedPropertyDescriptor ipd =
- (IndexedPropertyDescriptor) pd;
- Method readMethod = ipd.getIndexedReadMethod();
- Method publicReadMethod = getMatchingAccessibleMethod(readMethod, accessibleMethods);
- if (publicReadMethod != null && isAllowedToExpose(publicReadMethod)) {
- try {
- if (readMethod != publicReadMethod) {
- ipd = new IndexedPropertyDescriptor(
- ipd.getName(), ipd.getReadMethod(),
- null, publicReadMethod,
- null);
- }
- introspData.put(ipd.getName(), ipd);
- getArgTypesByMethod(introspData).put(publicReadMethod, publicReadMethod.getParameterTypes());
- } catch (IntrospectionException e) {
- LOG.warn("Failed creating a publicly-accessible property descriptor "
- + "for {} indexed property {}, read method {}",
- clazz.getName(), pd.getName(), publicReadMethod,
- e);
- }
- }
- } else {
- Method readMethod = pd.getReadMethod();
- Method publicReadMethod = getMatchingAccessibleMethod(readMethod, accessibleMethods);
- if (publicReadMethod != null && isAllowedToExpose(publicReadMethod)) {
- try {
- if (readMethod != publicReadMethod) {
- pd = new PropertyDescriptor(pd.getName(), publicReadMethod, null);
- pd.setReadMethod(publicReadMethod);
- }
- introspData.put(pd.getName(), pd);
- } catch (IntrospectionException e) {
- LOG.warn("Failed creating a publicly-accessible property descriptor "
- + "for {} property {}, read method {}",
- clazz.getName(), pd.getName(), publicReadMethod,
- e);
- }
- }
- }
- }
-
- private void addGenericGetToClassIntrospectionData(Map<Object, Object> introspData,
- Map<MethodSignature, List<Method>> accessibleMethods) {
- Method genericGet = getFirstAccessibleMethod(
- MethodSignature.GET_STRING_SIGNATURE, accessibleMethods);
- if (genericGet == null) {
- genericGet = getFirstAccessibleMethod(
- MethodSignature.GET_OBJECT_SIGNATURE, accessibleMethods);
- }
- if (genericGet != null) {
- introspData.put(GENERIC_GET_KEY, genericGet);
- }
- }
-
- private void addConstructorsToClassIntrospectionData(final Map<Object, Object> introspData,
- Class<?> clazz) {
- try {
- Constructor<?>[] ctors = clazz.getConstructors();
- if (ctors.length == 1) {
- Constructor<?> ctor = ctors[0];
- introspData.put(CONSTRUCTORS_KEY, new SimpleMethod(ctor, ctor.getParameterTypes()));
- } else if (ctors.length > 1) {
- OverloadedMethods overloadedCtors = new OverloadedMethods();
- for (Constructor<?> ctor : ctors) {
- overloadedCtors.addConstructor(ctor);
- }
- introspData.put(CONSTRUCTORS_KEY, overloadedCtors);
- }
- } catch (SecurityException e) {
- LOG.warn("Can't discover constructors for class {}", clazz.getName(), e);
- }
- }
-
- /**
- * Retrieves mapping of {@link MethodSignature}-s to a {@link List} of accessible methods for a class. In case the
- * class is not public, retrieves methods with same signature as its public methods from public superclasses and
- * interfaces. Basically upcasts every method to the nearest accessible method.
- */
- private static Map<MethodSignature, List<Method>> discoverAccessibleMethods(Class<?> clazz) {
- Map<MethodSignature, List<Method>> accessibles = new HashMap<>();
- discoverAccessibleMethods(clazz, accessibles);
- return accessibles;
- }
-
- private static void discoverAccessibleMethods(Class<?> clazz, Map<MethodSignature, List<Method>> accessibles) {
- if (Modifier.isPublic(clazz.getModifiers())) {
- try {
- Method[] methods = clazz.getMethods();
- for (Method method : methods) {
- MethodSignature sig = new MethodSignature(method);
- // Contrary to intuition, a class can actually have several
- // different methods with same signature *but* different
- // return types. These can't be constructed using Java the
- // language, as this is illegal on source code level, but
- // the compiler can emit synthetic methods as part of
- // generic type reification that will have same signature
- // yet different return type than an existing explicitly
- // declared method. Consider:
- // public interface I<T> { T m(); }
- // public class C implements I<Integer> { Integer m() { return 42; } }
- // C.class will have both "Object m()" and "Integer m()" methods.
- List<Method> methodList = accessibles.get(sig);
- if (methodList == null) {
- // TODO Collection.singletonList is more efficient, though read only.
- methodList = new LinkedList<>();
- accessibles.put(sig, methodList);
- }
- methodList.add(method);
- }
- return;
- } catch (SecurityException e) {
- LOG.warn("Could not discover accessible methods of class {}, attemping superclasses/interfaces.",
- clazz.getName(), e);
- // Fall through and attempt to discover superclass/interface methods
- }
- }
-
- Class<?>[] interfaces = clazz.getInterfaces();
- for (Class<?> anInterface : interfaces) {
- discoverAccessibleMethods(anInterface, accessibles);
- }
- Class<?> superclass = clazz.getSuperclass();
- if (superclass != null) {
- discoverAccessibleMethods(superclass, accessibles);
- }
- }
-
- private static Method getMatchingAccessibleMethod(Method m, Map<MethodSignature, List<Method>> accessibles) {
- if (m == null) {
- return null;
- }
- MethodSignature sig = new MethodSignature(m);
- List<Method> ams = accessibles.get(sig);
- if (ams == null) {
- return null;
- }
- for (Method am : ams) {
- if (am.getReturnType() == m.getReturnType()) {
- return am;
- }
- }
- return null;
- }
-
- private static Method getFirstAccessibleMethod(MethodSignature sig, Map<MethodSignature, List<Method>> accessibles) {
- List<Method> ams = accessibles.get(sig);
- if (ams == null || ams.isEmpty()) {
- return null;
- }
- return ams.get(0);
- }
-
- /**
- * As of this writing, this is only used for testing if method order really doesn't mater.
- */
- private MethodDescriptor[] sortMethodDescriptors(MethodDescriptor[] methodDescriptors) {
- return methodSorter != null ? methodSorter.sortMethodDescriptors(methodDescriptors) : methodDescriptors;
- }
-
- boolean isAllowedToExpose(Method method) {
- return exposureLevel < BeansWrapper.EXPOSE_SAFE || !UnsafeMethods.isUnsafeMethod(method);
- }
-
- private static Map<Method, Class<?>[]> getArgTypesByMethod(Map<Object, Object> classInfo) {
- @SuppressWarnings("unchecked")
- Map<Method, Class<?>[]> argTypes = (Map<Method, Class<?>[]>) classInfo.get(ARG_TYPES_BY_METHOD_KEY);
- if (argTypes == null) {
- argTypes = new HashMap<>();
- classInfo.put(ARG_TYPES_BY_METHOD_KEY, argTypes);
- }
- return argTypes;
- }
-
- private static final class MethodSignature {
- private static final MethodSignature GET_STRING_SIGNATURE =
- new MethodSignature("get", new Class[] { String.class });
- private static final MethodSignature GET_OBJECT_SIGNATURE =
- new MethodSignature("get", new Class[] { Object.class });
-
- private final String name;
- private final Class<?>[] args;
-
- private MethodSignature(String name, Class<?>[] args) {
- this.name = name;
- this.args = args;
- }
-
- MethodSignature(Method method) {
- this(method.getName(), method.getParameterTypes());
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof MethodSignature) {
- MethodSignature ms = (MethodSignature) o;
- return ms.name.equals(name) && Arrays.equals(args, ms.args);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return name.hashCode() ^ args.length; // TODO That's a poor quality hash... isn't this a problem?
- }
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Cache management:
-
- /**
- * Corresponds to {@link BeansWrapper#clearClassIntrospecitonCache()}.
- *
- * @since 2.3.20
- */
- void clearCache() {
- if (getHasSharedInstanceRestrictons()) {
- throw new IllegalStateException(
- "It's not allowed to clear the whole cache in a read-only " + getClass().getName() +
- "instance. Use removeFromClassIntrospectionCache(String prefix) instead.");
- }
- forcedClearCache();
- }
-
- private void forcedClearCache() {
- synchronized (sharedLock) {
- cache.clear();
- cacheClassNames.clear();
- clearingCounter++;
-
- for (WeakReference<Object> regedMfREf : modelFactories) {
- Object regedMf = regedMfREf.get();
- if (regedMf != null) {
- if (regedMf instanceof ClassBasedModelFactory) {
- ((ClassBasedModelFactory) regedMf).clearCache();
- } else if (regedMf instanceof ModelCache) {
- ((ModelCache) regedMf).clearCache();
- } else {
- throw new BugException();
- }
- }
- }
-
- removeClearedModelFactoryReferences();
- }
- }
-
- /**
- * Corresponds to {@link BeansWrapper#removeFromClassIntrospectionCache(Class)}.
- *
- * @since 2.3.20
- */
- void remove(Class<?> clazz) {
- synchronized (sharedLock) {
- cache.remove(clazz);
- cacheClassNames.remove(clazz.getName());
- clearingCounter++;
-
- for (WeakReference<Object> regedMfREf : modelFactories) {
- Object regedMf = regedMfREf.get();
- if (regedMf != null) {
- if (regedMf instanceof ClassBasedModelFactory) {
- ((ClassBasedModelFactory) regedMf).removeFromCache(clazz);
- } else if (regedMf instanceof ModelCache) {
- ((ModelCache) regedMf).clearCache(); // doesn't support selective clearing ATM
- } else {
- throw new BugException();
- }
- }
- }
-
- removeClearedModelFactoryReferences();
- }
- }
-
- /**
- * Returns the number of events so far that could make class introspection data returned earlier outdated.
- */
- int getClearingCounter() {
- synchronized (sharedLock) {
- return clearingCounter;
- }
- }
-
- private void onSameNameClassesDetected(String className) {
- // TODO: This behavior should be pluggable, as in environments where
- // some classes are often reloaded or multiple versions of the
- // same class is normal (OSGi), this will drop the cache contents
- // too often.
- LOG.info(
- "Detected multiple classes with the same name, \"{}\". "
- + "Assuming it was a class-reloading. Clearing class introspection caches to release old data.",
- className);
- forcedClearCache();
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Managing dependent objects:
-
- void registerModelFactory(ClassBasedModelFactory mf) {
- registerModelFactory((Object) mf);
- }
-
- void registerModelFactory(ModelCache mf) {
- registerModelFactory((Object) mf);
- }
-
- private void registerModelFactory(Object mf) {
- // Note that this `synchronized (sharedLock)` is also need for the BeansWrapper constructor to work safely.
- synchronized (sharedLock) {
- modelFactories.add(new WeakReference<>(mf, modelFactoriesRefQueue));
- removeClearedModelFactoryReferences();
- }
- }
-
- void unregisterModelFactory(ClassBasedModelFactory mf) {
- unregisterModelFactory((Object) mf);
- }
-
- void unregisterModelFactory(ModelCache mf) {
- unregisterModelFactory((Object) mf);
- }
-
- void unregisterModelFactory(Object mf) {
- synchronized (sharedLock) {
- for (Iterator<WeakReference<Object>> it = modelFactories.iterator(); it.hasNext(); ) {
- Object regedMf = it.next().get();
- if (regedMf == mf) {
- it.remove();
- }
- }
-
- }
- }
-
- private void removeClearedModelFactoryReferences() {
- Reference<?> cleardRef;
- while ((cleardRef = modelFactoriesRefQueue.poll()) != null) {
- synchronized (sharedLock) {
- findClearedRef: for (Iterator<WeakReference<Object>> it = modelFactories.iterator(); it.hasNext(); ) {
- if (it.next() == cleardRef) {
- it.remove();
- break findClearedRef;
- }
- }
- }
- }
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Extracting from introspection info:
-
- static Class<?>[] getArgTypes(Map<Object, Object> classInfo, Method method) {
- @SuppressWarnings("unchecked")
- Map<Method, Class<?>[]> argTypesByMethod = (Map<Method, Class<?>[]>) classInfo.get(ARG_TYPES_BY_METHOD_KEY);
- return argTypesByMethod.get(method);
- }
-
- /**
- * Returns the number of introspected methods/properties that should be available via the TemplateHashModel
- * interface.
- */
- int keyCount(Class<?> clazz) {
- Map<Object, Object> map = get(clazz);
- int count = map.size();
- if (map.containsKey(CONSTRUCTORS_KEY)) count--;
- if (map.containsKey(GENERIC_GET_KEY)) count--;
- if (map.containsKey(ARG_TYPES_BY_METHOD_KEY)) count--;
- return count;
- }
-
- /**
- * Returns the Set of names of introspected methods/properties that should be available via the TemplateHashModel
- * interface.
- */
- Set<Object> keySet(Class<?> clazz) {
- Set<Object> set = new HashSet<>(get(clazz).keySet());
- set.remove(CONSTRUCTORS_KEY);
- set.remove(GENERIC_GET_KEY);
- set.remove(ARG_TYPES_BY_METHOD_KEY);
- return set;
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Properties
-
- int getExposureLevel() {
- return exposureLevel;
- }
-
- boolean getExposeFields() {
- return exposeFields;
- }
-
- MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
- return methodAppearanceFineTuner;
- }
-
- MethodSorter getMethodSorter() {
- return methodSorter;
- }
-
- /**
- * Returns {@code true} if this instance was created with {@link ClassIntrospectorBuilder}, even if it wasn't
- * actually put into the cache (as we reserve the right to do so in later versions).
- */
- boolean getHasSharedInstanceRestrictons() {
- return hasSharedInstanceRestrictons;
- }
-
- /**
- * Tells if this instance is (potentially) shared among {@link BeansWrapper} instances.
- *
- * @see #getHasSharedInstanceRestrictons()
- */
- boolean isShared() {
- return shared;
- }
-
- /**
- * Almost always, you want to use {@link BeansWrapper#getSharedIntrospectionLock()}, not this! The only exception is
- * when you get this to set the field returned by {@link BeansWrapper#getSharedIntrospectionLock()}.
- */
- Object getSharedLock() {
- return sharedLock;
- }
-
- // -----------------------------------------------------------------------------------------------------------------
- // Monitoring:
-
- /** For unit testing only */
- Object[] getRegisteredModelFactoriesSnapshot() {
- synchronized (sharedLock) {
- return modelFactories.toArray();
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospectorBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospectorBuilder.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospectorBuilder.java
deleted file mode 100644
index c6f6cb6..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ClassIntrospectorBuilder.java
+++ /dev/null
@@ -1,190 +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.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.apache.freemarker.core.Version;
-import org.apache.freemarker.core.util._NullArgumentException;
-
-final class ClassIntrospectorBuilder implements Cloneable {
-
- private static final Map/*<PropertyAssignments, Reference<ClassIntrospector>>*/ INSTANCE_CACHE = new HashMap();
- private static final ReferenceQueue INSTANCE_CACHE_REF_QUEUE = new ReferenceQueue();
-
- // Properties and their *defaults*:
- private int exposureLevel = BeansWrapper.EXPOSE_SAFE;
- private boolean exposeFields;
- private MethodAppearanceFineTuner methodAppearanceFineTuner;
- private MethodSorter methodSorter;
- // Attention:
- // - This is also used as a cache key, so non-normalized field values should be avoided.
- // - If some field has a default value, it must be set until the end of the constructor. No field that has a
- // default can be left unset (like null).
- // - If you add a new field, review all methods in this class, also the ClassIntrospector constructor
-
- ClassIntrospectorBuilder(ClassIntrospector ci) {
- exposureLevel = ci.exposureLevel;
- exposeFields = ci.exposeFields;
- methodAppearanceFineTuner = ci.methodAppearanceFineTuner;
- methodSorter = ci.methodSorter;
- }
-
- ClassIntrospectorBuilder(Version incompatibleImprovements) {
- // Warning: incompatibleImprovements must not affect this object at versions increments where there's no
- // change in the BeansWrapper.normalizeIncompatibleImprovements results. That is, this class may don't react
- // to some version changes that affects BeansWrapper, but not the other way around.
- _NullArgumentException.check(incompatibleImprovements);
- // Currently nothing depends on incompatibleImprovements
- }
-
- @Override
- protected Object clone() {
- try {
- return super.clone();
- } catch (CloneNotSupportedException e) {
- throw new RuntimeException("Failed to clone ClassIntrospectorBuilder", e);
- }
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + (exposeFields ? 1231 : 1237);
- result = prime * result + exposureLevel;
- result = prime * result + System.identityHashCode(methodAppearanceFineTuner);
- result = prime * result + System.identityHashCode(methodSorter);
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
- ClassIntrospectorBuilder other = (ClassIntrospectorBuilder) obj;
-
- if (exposeFields != other.exposeFields) return false;
- if (exposureLevel != other.exposureLevel) return false;
- if (methodAppearanceFineTuner != other.methodAppearanceFineTuner) return false;
- return methodSorter == other.methodSorter;
- }
-
- public int getExposureLevel() {
- return exposureLevel;
- }
-
- /** See {@link BeansWrapper#setExposureLevel(int)}. */
- public void setExposureLevel(int exposureLevel) {
- if (exposureLevel < BeansWrapper.EXPOSE_ALL || exposureLevel > BeansWrapper.EXPOSE_NOTHING) {
- throw new IllegalArgumentException("Illegal exposure level: " + exposureLevel);
- }
-
- this.exposureLevel = exposureLevel;
- }
-
- public boolean getExposeFields() {
- return exposeFields;
- }
-
- /** See {@link BeansWrapper#setExposeFields(boolean)}. */
- public void setExposeFields(boolean exposeFields) {
- this.exposeFields = exposeFields;
- }
-
- public MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
- return methodAppearanceFineTuner;
- }
-
- public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner methodAppearanceFineTuner) {
- this.methodAppearanceFineTuner = methodAppearanceFineTuner;
- }
-
- public MethodSorter getMethodSorter() {
- return methodSorter;
- }
-
- public void setMethodSorter(MethodSorter methodSorter) {
- this.methodSorter = methodSorter;
- }
-
- private static void removeClearedReferencesFromInstanceCache() {
- Reference clearedRef;
- while ((clearedRef = INSTANCE_CACHE_REF_QUEUE.poll()) != null) {
- synchronized (INSTANCE_CACHE) {
- findClearedRef: for (Iterator it = INSTANCE_CACHE.values().iterator(); it.hasNext(); ) {
- if (it.next() == clearedRef) {
- it.remove();
- break findClearedRef;
- }
- }
- }
- }
- }
-
- /** For unit testing only */
- static void clearInstanceCache() {
- synchronized (INSTANCE_CACHE) {
- INSTANCE_CACHE.clear();
- }
- }
-
- /** For unit testing only */
- static Map getInstanceCache() {
- return INSTANCE_CACHE;
- }
-
- /**
- * Returns an instance that is possibly shared (singleton). Note that this comes with its own "shared lock",
- * since everyone who uses this object will have to lock with that common object.
- */
- ClassIntrospector build() {
- if ((methodAppearanceFineTuner == null || methodAppearanceFineTuner instanceof SingletonCustomizer)
- && (methodSorter == null || methodSorter instanceof SingletonCustomizer)) {
- // Instance can be cached.
- ClassIntrospector instance;
- synchronized (INSTANCE_CACHE) {
- Reference instanceRef = (Reference) INSTANCE_CACHE.get(this);
- instance = instanceRef != null ? (ClassIntrospector) instanceRef.get() : null;
- if (instance == null) {
- ClassIntrospectorBuilder thisClone = (ClassIntrospectorBuilder) clone(); // prevent any aliasing issues
- instance = new ClassIntrospector(thisClone, new Object(), true, true);
- INSTANCE_CACHE.put(thisClone, new WeakReference(instance, INSTANCE_CACHE_REF_QUEUE));
- }
- }
-
- removeClearedReferencesFromInstanceCache();
-
- return instance;
- } else {
- // If methodAppearanceFineTuner or methodSorter is specified and isn't marked as a singleton, the
- // ClassIntrospector can't be shared/cached as those objects could contain a back-reference to the
- // BeansWrapper.
- return new ClassIntrospector(this, new Object(), true, false);
- }
- }
-
-}
\ 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/CollectionAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/CollectionAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/CollectionAdapter.java
deleted file mode 100644
index 74db4cb..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/CollectionAdapter.java
+++ /dev/null
@@ -1,88 +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.util.AbstractCollection;
-import java.util.Collection;
-import java.util.Iterator;
-
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelAdapter;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.util.UndeclaredThrowableException;
-
-/**
- * Adapts a {@link TemplateCollectionModel} to {@link Collection}.
- */
-class CollectionAdapter extends AbstractCollection implements TemplateModelAdapter {
- private final BeansWrapper wrapper;
- private final TemplateCollectionModel model;
-
- CollectionAdapter(TemplateCollectionModel model, BeansWrapper wrapper) {
- this.model = model;
- this.wrapper = wrapper;
- }
-
- @Override
- public TemplateModel getTemplateModel() {
- return model;
- }
-
- @Override
- public int size() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Iterator iterator() {
- try {
- return new Iterator() {
- final TemplateModelIterator i = model.iterator();
-
- @Override
- public boolean hasNext() {
- try {
- return i.hasNext();
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public Object next() {
- try {
- return wrapper.unwrap(i.next());
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/CollectionModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/CollectionModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/CollectionModel.java
deleted file mode 100644
index d27a56d..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/CollectionModel.java
+++ /dev/null
@@ -1,109 +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.util.Collection;
-import java.util.List;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-
-/**
- * <p>A special case of {@link BeanModel} that can wrap Java collections
- * and that implements the {@link TemplateCollectionModel} in order to be usable
- * in a <tt><#list></tt> block.</p>
- */
-public class CollectionModel
-extends
- StringModel
-implements
- TemplateCollectionModel,
- TemplateSequenceModel {
- static final ModelFactory FACTORY =
- new ModelFactory()
- {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new CollectionModel((Collection) object, (BeansWrapper) wrapper);
- }
- };
-
-
- /**
- * Creates a new model that wraps the specified collection object.
- * @param collection the collection object to wrap into a model.
- * @param wrapper the {@link BeansWrapper} associated with this model.
- * Every model has to have an associated {@link BeansWrapper} instance. The
- * model gains many attributes from its wrapper, including the caching
- * behavior, method exposure level, method-over-item shadowing policy etc.
- */
- public CollectionModel(Collection collection, BeansWrapper wrapper) {
- super(collection, wrapper);
- }
-
- /**
- * Retrieves the i-th object from the collection, wrapped as a TemplateModel.
- * @throws TemplateModelException if the index is out of bounds, or the
- * underlying collection is not a List.
- */
- @Override
- public TemplateModel get(int index)
- throws TemplateModelException {
- // Don't forget to keep getSupportsIndexedAccess in sync with this!
- if (object instanceof List) {
- try {
- return wrap(((List) object).get(index));
- } catch (IndexOutOfBoundsException e) {
- return null;
-// throw new TemplateModelException("Index out of bounds: " + index);
- }
- } else {
- throw new TemplateModelException("Underlying collection is not a list, it's " + object.getClass().getName());
- }
- }
-
- /**
- * Tells if {@link #get(int)} will always fail for this object.
- * As this object implements {@link TemplateSequenceModel},
- * {@link #get(int)} should always work, but due to a design flaw, for
- * non-{@link List} wrapped objects {@link #get(int)} will always fail.
- * This method exists to ease working this problem around.
- *
- * @since 2.3.17
- */
- public boolean getSupportsIndexedAccess() {
- return object instanceof List;
- }
-
- @Override
- public TemplateModelIterator iterator() {
- return new IteratorModel(((Collection) object).iterator(), wrapper);
- }
-
- @Override
- public int size() {
- return ((Collection) object).size();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/DateModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/DateModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/DateModel.java
deleted file mode 100644
index 55419cd..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/DateModel.java
+++ /dev/null
@@ -1,79 +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.util.Date;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateDateModel;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Wraps arbitrary subclass of {@link java.util.Date} into a reflective model.
- * Beside acting as a {@link TemplateDateModel}, you can call all Java methods
- * on these objects as well.
- */
-public class DateModel
-extends
- BeanModel
-implements
- TemplateDateModel {
- static final ModelFactory FACTORY =
- new ModelFactory()
- {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new DateModel((Date) object, (BeansWrapper) wrapper);
- }
- };
-
- private final int type;
-
- /**
- * Creates a new model that wraps the specified date object.
- * @param date the date object to wrap into a model.
- * @param wrapper the {@link BeansWrapper} associated with this model.
- * Every model has to have an associated {@link BeansWrapper} instance. The
- * model gains many attributes from its wrapper, including the caching
- * behavior, method exposure level, method-over-item shadowing policy etc.
- */
- public DateModel(Date date, BeansWrapper wrapper) {
- super(date, wrapper);
- if (date instanceof java.sql.Date) {
- type = DATE;
- } else if (date instanceof java.sql.Time) {
- type = TIME;
- } else if (date instanceof java.sql.Timestamp) {
- type = DATETIME;
- } else {
- type = wrapper.getDefaultDateType();
- }
- }
-
- @Override
- public Date getAsDate() {
- return (Date) object;
- }
-
- @Override
- public int getDateType() {
- return type;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/EmptyCallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/EmptyCallableMemberDescriptor.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/EmptyCallableMemberDescriptor.java
deleted file mode 100644
index d0e6b8e..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/EmptyCallableMemberDescriptor.java
+++ /dev/null
@@ -1,35 +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;
-
-/**
- * Represents that no member was chosen. Why it wasn't is represented by the two singleton instances,
- * {@link #NO_SUCH_METHOD} and {@link #AMBIGUOUS_METHOD}. (Note that instances of these are cached associated with the
- * argument types, thus it shouldn't store details that are specific to the actual argument values. In fact, it better
- * remains a set of singletons.)
- */
-final class EmptyCallableMemberDescriptor extends MaybeEmptyCallableMemberDescriptor {
-
- static final EmptyCallableMemberDescriptor NO_SUCH_METHOD = new EmptyCallableMemberDescriptor();
- static final EmptyCallableMemberDescriptor AMBIGUOUS_METHOD = new EmptyCallableMemberDescriptor();
-
- private EmptyCallableMemberDescriptor() { }
-
-}
\ 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/EmptyMemberAndArguments.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/EmptyMemberAndArguments.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/EmptyMemberAndArguments.java
deleted file mode 100644
index 2e078c9..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/EmptyMemberAndArguments.java
+++ /dev/null
@@ -1,93 +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 org.apache.freemarker.core._DelayedOrdinal;
-
-/**
- * Describes a failed member lookup. Instances of this must not be cached as instances may store the actual argument
- * values.
- */
-final class EmptyMemberAndArguments extends MaybeEmptyMemberAndArguments {
-
- static final EmptyMemberAndArguments WRONG_NUMBER_OF_ARGUMENTS
- = new EmptyMemberAndArguments(
- "No compatible overloaded variation was found; wrong number of arguments.", true, null);
-
- private final Object errorDescription;
- private final boolean numberOfArgumentsWrong;
- private final Object[] unwrappedArguments;
-
- private EmptyMemberAndArguments(
- Object errorDescription, boolean numberOfArgumentsWrong, Object[] unwrappedArguments) {
- this.errorDescription = errorDescription;
- this.numberOfArgumentsWrong = numberOfArgumentsWrong;
- this.unwrappedArguments = unwrappedArguments;
- }
-
- static EmptyMemberAndArguments noCompatibleOverload(int unwrappableIndex) {
- return new EmptyMemberAndArguments(
- new Object[] { "No compatible overloaded variation was found; can't convert (unwrap) the ",
- new _DelayedOrdinal(Integer.valueOf(unwrappableIndex)), " argument to the desired Java type." },
- false,
- null);
- }
-
- static EmptyMemberAndArguments noCompatibleOverload(Object[] unwrappedArgs) {
- return new EmptyMemberAndArguments(
- "No compatible overloaded variation was found; declared parameter types and argument value types mismatch.",
- false,
- unwrappedArgs);
- }
-
- static EmptyMemberAndArguments ambiguous(Object[] unwrappedArgs) {
- return new EmptyMemberAndArguments(
- "Multiple compatible overloaded variations were found with the same priority.",
- false,
- unwrappedArgs);
- }
-
- static MaybeEmptyMemberAndArguments from(
- EmptyCallableMemberDescriptor emtpyMemberDesc, Object[] unwrappedArgs) {
- if (emtpyMemberDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) {
- return noCompatibleOverload(unwrappedArgs);
- } else if (emtpyMemberDesc == EmptyCallableMemberDescriptor.AMBIGUOUS_METHOD) {
- return ambiguous(unwrappedArgs);
- } else {
- throw new IllegalArgumentException("Unrecognized constant: " + emtpyMemberDesc);
- }
- }
-
- Object getErrorDescription() {
- return errorDescription;
- }
-
- /**
- * @return {@code null} if the error has occurred earlier than the full argument list was unwrapped.
- */
- Object[] getUnwrappedArguments() {
- return unwrappedArguments;
- }
-
- public boolean isNumberOfArgumentsWrong() {
- return numberOfArgumentsWrong;
- }
-
-}
[18/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/DefaultEnumerationAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DefaultEnumerationAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/DefaultEnumerationAdapter.java
new file mode 100644
index 0000000..ec20b9f
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/DefaultEnumerationAdapter.java
@@ -0,0 +1,121 @@
+/*
+ * 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.Enumeration;
+import java.util.Iterator;
+
+import org.apache.freemarker.core.model.AdapterTemplateModel;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.model.WrapperTemplateModel;
+import org.apache.freemarker.core.model.WrappingTemplateModel;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Adapts an {@link Enumeration} to the corresponding {@link TemplateModel} interface(s), most importantly to
+ * {@link TemplateCollectionModel}. Putting aside that it wraps an {@link Enumeration} instead of an {@link Iterator},
+ * this is identical to {@link DefaultIteratorAdapter}, so see further details there.
+ */
+// TODO JUnit
+public class DefaultEnumerationAdapter extends WrappingTemplateModel implements TemplateCollectionModel,
+ AdapterTemplateModel, WrapperTemplateModel, Serializable {
+
+ @SuppressFBWarnings(value="SE_BAD_FIELD", justification="We hope it's Seralizable")
+ private final Enumeration enumeration;
+ private boolean enumerationOwnedBySomeone;
+
+ /**
+ * Factory method for creating new adapter instances.
+ *
+ * @param iterator
+ * The enumeration to adapt; can't be {@code null}.
+ */
+ public static DefaultEnumerationAdapter adapt(Enumeration iterator, ObjectWrapper wrapper) {
+ return new DefaultEnumerationAdapter(iterator, wrapper);
+ }
+
+ private DefaultEnumerationAdapter(Enumeration enumeration, ObjectWrapper wrapper) {
+ super(wrapper);
+ this.enumeration = enumeration;
+ }
+
+ @Override
+ public Object getWrappedObject() {
+ return enumeration;
+ }
+
+ @Override
+ public Object getAdaptedObject(Class hint) {
+ return getWrappedObject();
+ }
+
+ @Override
+ public TemplateModelIterator iterator() throws TemplateModelException {
+ return new SimpleTemplateModelIterator();
+ }
+
+ /**
+ * Not thread-safe.
+ */
+ private class SimpleTemplateModelIterator implements TemplateModelIterator {
+
+ private boolean enumerationOwnedByMe;
+
+ @Override
+ public TemplateModel next() throws TemplateModelException {
+ if (!enumerationOwnedByMe) {
+ checkNotOwner();
+ enumerationOwnedBySomeone = true;
+ enumerationOwnedByMe = true;
+ }
+
+ if (!enumeration.hasMoreElements()) {
+ throw new TemplateModelException("The collection has no more items.");
+ }
+
+ Object value = enumeration.nextElement();
+ return value instanceof TemplateModel ? (TemplateModel) value : wrap(value);
+ }
+
+ @Override
+ public boolean hasNext() throws TemplateModelException {
+ // Calling hasNext may looks safe, but I have met sync. problems.
+ if (!enumerationOwnedByMe) {
+ checkNotOwner();
+ }
+
+ return enumeration.hasMoreElements();
+ }
+
+ private void checkNotOwner() throws TemplateModelException {
+ if (enumerationOwnedBySomeone) {
+ throw new TemplateModelException(
+ "This collection value wraps a java.util.Enumeration, thus it can be listed only once.");
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
index 0351edd..ebc5a3d 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
@@ -19,22 +19,52 @@
package org.apache.freemarker.core.model.impl;
+import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
-import java.util.ArrayList;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
import org.apache.freemarker.core.Configuration;
import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core._CoreAPI;
+import org.apache.freemarker.core._CoreLogs;
+import org.apache.freemarker.core._DelayedFTLTypeDescription;
+import org.apache.freemarker.core._DelayedShortClassName;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.AdapterTemplateModel;
import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.RichObjectWrapper;
import org.apache.freemarker.core.model.TemplateBooleanModel;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapperConfiguration;
+import org.apache.freemarker.core.model.TemplateNumberModel;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.WrapperTemplateModel;
+import org.apache.freemarker.core.util.BugException;
+import org.apache.freemarker.core.util.WriteProtectable;
+import org.apache.freemarker.core.util._ClassUtil;
import org.apache.freemarker.dom.NodeModel;
+import org.slf4j.Logger;
import org.w3c.dom.Node;
/**
@@ -54,46 +84,583 @@ import org.w3c.dom.Node;
* JSR 133 and related literature). When used as part of {@link Configuration}, of course it's enough if that was safely
* published and then left unmodified.
*/
-public class DefaultObjectWrapper extends org.apache.freemarker.core.model.impl.beans.BeansWrapper {
-
+// TODO Get rid of unused stuff (including other clases in this package) coming from BeansWrapper.
+public class DefaultObjectWrapper implements RichObjectWrapper, WriteProtectable {
+
+ private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER;
+
+ /**
+ * 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;
+
+ // -----------------------------------------------------------------------------------------------------------------
+ // Introspection cache:
+
+ private final Object sharedIntrospectionLock;
+
+ /**
+ * {@link Class} to class info cache.
+ * This object is possibly shared with other {@link DefaultObjectWrapper}-s!
+ *
+ * <p>To write this, always use {@link #replaceClassIntrospector(ClassIntrospectorBuilder)}.
+ *
+ * <p>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;
+
+ /**
+ * {@link String} class name to {@link StaticModel} cache.
+ * This object only belongs to a single {@link DefaultObjectWrapper}.
+ * This has to be final as {@link #getStaticModels()} might returns it any time and then it has to remain a good
+ * reference.
+ */
+ private final StaticModels staticModels;
+
/**
- * Use {@link DefaultObjectWrapperBuilder} instead if possible. Instances created with this constructor won't share
- * the class introspection caches with other instances. See {@link BeansWrapper#BeansWrapper(Version)} (the
- * superclass constructor) for more details.
- *
+ * {@link String} class name to {@link EnumerationModel} cache.
+ * This object only belongs to a single {@link DefaultObjectWrapper}.
+ * This has to be final as {@link #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 {@link DefaultObjectWrapper}.
+ */
+ 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 int defaultDateType; // initialized by PropertyAssignments.apply
+ private ObjectWrapper outerIdentity = this;
+ private boolean methodsShadowItems = true;
+ private boolean strict; // initialized by PropertyAssignments.apply
+
+ private final Version incompatibleImprovements;
+
+ /**
+ * Use {@link DefaultObjectWrapperBuilder} 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
+ *
* @param incompatibleImprovements
- * It's the same as in {@link BeansWrapper#BeansWrapper(Version)}.
- *
+ * Sets which of the non-backward-compatible improvements should be enabled. Not {@code null}. This version number
+ * is the same as the FreeMarker version number with which the improvements were implemented.
+ *
+ * <p>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.
+ *
+ * <p>The reason it's separate from {@link Configuration#setIncompatibleImprovements(Version)} is that
+ * {@link ObjectWrapper} objects are often shared among multiple {@link Configuration}-s, so the two version
+ * numbers are technically independent. But it's recommended to keep those two version numbers the same.
+ *
+ * <p>The changes enabled by {@code incompatibleImprovements} are:
+ * <ul>
+ * <li>
+ * <p>2.3.0: No changes; this is the starting point, the version used in older projects.
+ * </li>
+ * <li>
+ * <p>2.3.21 (or higher):
+ * Several glitches were oms in <em>overloaded</em> 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 {@code null} arguments were oms, as earlier they were only seen
+ * applicable to parameters of type {@code Object}. Now {@code 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 {@code Object} parameter type. For more details
+ * about overloaded method selection changes see the version history in the FreeMarker Manual.
+ * </li>
+ * <li>
+ * <p>2.3.24 (or higher):
+ * {@link Iterator}-s were always said to be non-empty when using {@code ?has_content} and such (i.e.,
+ * operators that check emptiness without reading any elements). Now an {@link Iterator} counts as
+ * empty exactly if it has no elements left. (Note that this bug has never affected basic functionality, like
+ * {@code <#list ...>}.)
+ * </li>
+ * </ul>
+ *
+ * <p>Note that the version will be normalized to the lowest version where the same incompatible
+ * {@link DefaultObjectWrapper} improvements were already present, so {@link #getIncompatibleImprovements()} might returns
+ * a lower version than what you have specified.
+ *
* @since 2.3.21
*/
public DefaultObjectWrapper(Version incompatibleImprovements) {
- this(new DefaultObjectWrapperConfiguration(incompatibleImprovements) { }, false);
+ this(new DefaultObjectWrapperConfiguration(incompatibleImprovements) {}, false);
+ // Attention! Don't don anything here, as the instance is possibly already visible to other threads through the
+ // model factory callbacks.
}
/**
- * Use {@link #DefaultObjectWrapper(DefaultObjectWrapperConfiguration, boolean)} instead if possible;
- * it does the same, except that it tolerates a non-{@link DefaultObjectWrapperConfiguration} configuration too.
- *
+ * Same as {@link #DefaultObjectWrapper(DefaultObjectWrapperConfiguration, boolean, boolean)} with {@code true}
+ * {@code finalizeConstruction} argument.
+ *
* @since 2.3.21
*/
- protected DefaultObjectWrapper(BeansWrapperConfiguration bwCfg, boolean writeProtected) {
- super(bwCfg, writeProtected, false);
- DefaultObjectWrapperConfiguration dowDowCfg = bwCfg instanceof DefaultObjectWrapperConfiguration
- ? (DefaultObjectWrapperConfiguration) bwCfg
- : new DefaultObjectWrapperConfiguration(bwCfg.getIncompatibleImprovements()) { };
+ protected DefaultObjectWrapper(DefaultObjectWrapperConfiguration bwConf, boolean writeProtected) {
+ this(bwConf, writeProtected, true);
+ }
+
+ /**
+ * Initializes the instance based on the the {@link DefaultObjectWrapperConfiguration} specified.
+ *
+ * @param writeProtected Makes the instance's configuration settings read-only via
+ * {@link WriteProtectable#writeProtect()}; this way it can use the shared class introspection cache.
+ *
+ * @param finalizeConstruction Decides if the construction is finalized now, or the caller will do some more
+ * adjustments on the instance and then call {@link #finalizeConstruction(boolean)} itself.
+ *
+ * @since 2.3.22
+ */
+ protected DefaultObjectWrapper(DefaultObjectWrapperConfiguration bwConf, boolean writeProtected, boolean finalizeConstruction) {
+ incompatibleImprovements = bwConf.getIncompatibleImprovements(); // normalized
+
+ defaultDateType = bwConf.getDefaultDateType();
+ outerIdentity = bwConf.getOuterIdentity() != null ? bwConf.getOuterIdentity() : this;
+ strict = bwConf.isStrict();
+
+ if (!writeProtected) {
+ // As this is not a read-only DefaultObjectWrapper, 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.
+ sharedIntrospectionLock = new Object();
+ classIntrospector = new ClassIntrospector(bwConf.classIntrospectorFactory, sharedIntrospectionLock);
+ } else {
+ // As this is a read-only DefaultObjectWrapper, the classIntrospector is never replaced, and since it's shared by
+ // other DefaultObjectWrapper instances, we use the lock belonging to the shared ClassIntrospector.
+ classIntrospector = bwConf.classIntrospectorFactory.build();
+ sharedIntrospectionLock = classIntrospector.getSharedLock();
+ }
+
+ falseModel = new BooleanModel(Boolean.FALSE, this);
+ trueModel = new BooleanModel(Boolean.TRUE, this);
+
+ staticModels = new StaticModels(this);
+ enumModels = new _EnumModels(this);
+ modelCache = new BeansModelCache(this);
+ setUseModelCache(bwConf.getUseModelCache());
+
finalizeConstruction(writeProtected);
}
/**
- * Calls {@link BeansWrapper#BeansWrapper(BeansWrapperConfiguration, boolean)} and sets up
- * {@link DefaultObjectWrapper}-specific fields.
- *
+ * Meant to be called after {@link DefaultObjectWrapper#DefaultObjectWrapper(DefaultObjectWrapperConfiguration, boolean, boolean)} when
+ * its last argument was {@code 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
+ * {@code writeProtected} was {@code true}.
+ *
* @since 2.3.22
*/
- protected DefaultObjectWrapper(DefaultObjectWrapperConfiguration dowCfg, boolean writeProtected) {
- this((BeansWrapperConfiguration) dowCfg, writeProtected);
+ protected void finalizeConstruction(boolean writeProtected) {
+ if (writeProtected) {
+ writeProtect();
+ }
+
+ // Attention! At this point, the DefaultObjectWrapper must be fully initialized, as when the model factories are
+ // registered below, the DefaultObjectWrapper can immediately get concurrent callbacks. That those other threads will
+ // see consistent image of the DefaultObjectWrapper is ensured that callbacks are always sync-ed on
+ // classIntrospector.sharedLock, and so is classIntrospector.registerModelFactory(...).
+
+ registerModelFactories();
+ }
+
+ /**
+ * Makes the configuration properties (settings) of this {@link DefaultObjectWrapper} 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.
+ *
+ * <p>Consider using {@link DefaultObjectWrapperBuilder} instead, which gives an instance that's already
+ * write protected and also uses some shared caches/pools.
+ *
+ * @since 2.3.21
+ */
+ @Override
+ public void writeProtect() {
+ writeProtected = true;
+ }
+
+ /**
+ * @since 2.3.21
+ */
+ @Override
+ public boolean isWriteProtected() {
+ return writeProtected;
+ }
+
+ Object getSharedIntrospectionLock() {
+ return sharedIntrospectionLock;
+ }
+
+ /**
+ * If this object is already read-only according to {@link WriteProtectable}, throws {@link IllegalStateException},
+ * otherwise does nothing.
+ *
+ * @since 2.3.21
+ */
+ protected void checkModifiable() {
+ if (writeProtected) throw new IllegalStateException(
+ "Can't modify the " + getClass().getName() + " object, as it was write protected.");
+ }
+
+ /**
+ * @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 you are doing.
+ */
+ public void setStrict(boolean strict) {
+ checkModifiable();
+ this.strict = strict;
+ }
+
+ /**
+ * When wrapping an object, the DefaultObjectWrapper 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 DefaultObjectWrapper 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) {
+ checkModifiable();
+ this.outerIdentity = outerIdentity;
+ }
+
+ /**
+ * By default returns <tt>this</tt>.
+ * @see #setOuterIdentity(ObjectWrapper)
+ */
+ public ObjectWrapper getOuterIdentity() {
+ return outerIdentity;
+ }
+
+ // 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 <code>EXPOSE_SAFE</code>.
+ * @param exposureLevel can be any of the <code>EXPOSE_xxx</code>
+ * constants.
+ */
+ public void setExposureLevel(int exposureLevel) {
+ checkModifiable();
+
+ if (classIntrospector.getExposureLevel() != exposureLevel) {
+ ClassIntrospectorBuilder pa = classIntrospector.getPropertyAssignments();
+ pa.setExposureLevel(exposureLevel);
+ replaceClassIntrospector(pa);
+ }
+ }
+
+ /**
+ * @since 2.3.21
+ */
+ public int getExposureLevel() {
+ return classIntrospector.getExposureLevel();
+ }
+
+ /**
+ * Controls whether public instance fields of classes are exposed to
+ * templates.
+ * @param 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 (classIntrospector.getExposeFields() != exposeFields) {
+ ClassIntrospectorBuilder pa = classIntrospector.getPropertyAssignments();
+ pa.setExposeFields(exposeFields);
+ replaceClassIntrospector(pa);
+ }
+ }
+
+ /**
+ * Returns whether exposure of public instance fields of classes is
+ * enabled. See {@link #setExposeFields(boolean)} for details.
+ * @return true if public instance fields are exposed, false otherwise.
+ */
+ public boolean isExposeFields() {
+ return classIntrospector.getExposeFields();
+ }
+
+ public MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
+ return classIntrospector.getMethodAppearanceFineTuner();
+ }
+
+ /**
+ * Used to tweak certain aspects of how methods appear in the data-model;
+ * see {@link MethodAppearanceFineTuner} for more.
+ */
+ public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner methodAppearanceFineTuner) {
+ checkModifiable();
+
+ if (classIntrospector.getMethodAppearanceFineTuner() != methodAppearanceFineTuner) {
+ ClassIntrospectorBuilder pa = classIntrospector.getPropertyAssignments();
+ pa.setMethodAppearanceFineTuner(methodAppearanceFineTuner);
+ replaceClassIntrospector(pa);
+ }
+ }
+
+ MethodSorter getMethodSorter() {
+ return classIntrospector.getMethodSorter();
+ }
+
+ void setMethodSorter(MethodSorter methodSorter) {
+ checkModifiable();
+
+ if (classIntrospector.getMethodSorter() != methodSorter) {
+ ClassIntrospectorBuilder pa = classIntrospector.getPropertyAssignments();
+ pa.setMethodSorter(methodSorter);
+ replaceClassIntrospector(pa);
+ }
+ }
+
+ /**
+ * Tells if this instance acts like if its class introspection cache is sharable with other {@link DefaultObjectWrapper}-s.
+ * A restricted cache denies certain too "antisocial" operations, like {@link #clearClassIntrospecitonCache()}.
+ * The value depends on how the instance
+ * was created; with a public constructor (then this is {@code false}), or with {@link DefaultObjectWrapperBuilder}
+ * (then it's {@code 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 {@code true} even then.
+ *
+ * @since 2.3.21
+ */
+ public boolean isClassIntrospectionCacheRestricted() {
+ return classIntrospector.getHasSharedInstanceRestrictons();
+ }
+
+ /**
+ * Replaces the value of {@link #classIntrospector}, but first it unregisters
+ * the model factories in the old {@link #classIntrospector}.
+ */
+ private void replaceClassIntrospector(ClassIntrospectorBuilder pa) {
+ checkModifiable();
+
+ final ClassIntrospector newCI = new ClassIntrospector(pa, sharedIntrospectionLock);
+ 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 (sharedIntrospectionLock) {
+ oldCI = classIntrospector;
+ if (oldCI != null) {
+ // Note that after unregistering the model factory might still gets some callback from the old
+ // classIntrospector
+ if (staticModels != null) {
+ oldCI.unregisterModelFactory(staticModels);
+ staticModels.clearCache();
+ }
+ if (enumModels != null) {
+ oldCI.unregisterModelFactory(enumModels);
+ enumModels.clearCache();
+ }
+ if (modelCache != null) {
+ oldCI.unregisterModelFactory(modelCache);
+ modelCache.clearCache();
+ }
+ if (trueModel != null) {
+ trueModel.clearMemberCache();
+ }
+ if (falseModel != null) {
+ falseModel.clearMemberCache();
+ }
+ }
+
+ classIntrospector = newCI;
+
+ registerModelFactories();
+ }
+ }
+
+ private void registerModelFactories() {
+ if (staticModels != null) {
+ classIntrospector.registerModelFactory(staticModels);
+ }
+ if (enumModels != null) {
+ classIntrospector.registerModelFactory(enumModels);
+ }
+ if (modelCache != null) {
+ classIntrospector.registerModelFactory(modelCache);
+ }
+ }
+
+ /**
+ * Sets whether methods shadow items in beans. When true (this is the
+ * default value), <code>${object.name}</code> 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
+ * <code>object.get(name)</code>, 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 = methodsShadowItems;
+ }
+ }
+
+ boolean isMethodsShadowItems() {
+ return methodsShadowItems;
+ }
+
+ /**
+ * Sets the default date type to use for date models that result from
+ * a plain <tt>java.util.Date</tt> instead of <tt>java.sql.Date</tt> or
+ * <tt>java.sql.Time</tt> or <tt>java.sql.Timestamp</tt>. Default value is
+ * {@link TemplateDateModel#UNKNOWN}.
+ * @param 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 = defaultDateType;
+ }
+ }
+
+ /**
+ * Returns the default date type. See {@link #setDefaultDateType(int)} for
+ * details.
+ * @return the default date type
+ */
+ public int getDefaultDateType() {
+ return defaultDateType;
+ }
+
+ /**
+ * Sets whether this wrapper caches the {@link TemplateModel}-s created for the Java objects that has wrapped with
+ * this object wrapper. Default is {@code false}.
+ * When set to {@code true}, calling {@link #wrap(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 setUseModelCache(boolean useCache) {
+ checkModifiable();
+ modelCache.setUseCache(useCache);
+ }
+
+ /**
+ * @since 2.3.21
+ */
+ public boolean getUseModelCache() {
+ return modelCache.getUseCache();
+ }
+
+ /**
+ * Returns the version given with {@link #DefaultObjectWrapper(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 incompatibleImprovements;
+ }
+
/**
* Wraps the parameter object to {@link TemplateModel} interface(s). Simple types like numbers, strings, booleans
* and dates will be wrapped into the corresponding {@code SimpleXxx} classes (like {@link SimpleNumber}).
@@ -104,11 +671,15 @@ public class DefaultObjectWrapper extends org.apache.freemarker.core.model.impl.
@Override
public TemplateModel wrap(Object obj) throws TemplateModelException {
if (obj == null) {
- return super.wrap(null);
+ return null;
}
if (obj instanceof TemplateModel) {
return (TemplateModel) obj;
}
+ if (obj instanceof TemplateModelAdapter) {
+ return ((TemplateModelAdapter) obj).getTemplateModel();
+ }
+
if (obj instanceof String) {
return new SimpleScalar((String) obj);
}
@@ -148,68 +719,847 @@ public class DefaultObjectWrapper extends org.apache.freemarker.core.model.impl.
if (obj instanceof Iterable) {
return DefaultIterableAdapter.adapt((Iterable<?>) obj, this);
}
+ if (obj instanceof Enumeration) {
+ return DefaultEnumerationAdapter.adapt((Enumeration<?>) obj, this);
+ }
+
+ // [FM3] Via plugin mechanism, not by default anymore
+ if (obj instanceof Node) {
+ return handW3CNode((Node) obj);
+ }
+
return handleUnknownType(obj);
}
-
+
+ protected TemplateModel handW3CNode(Node node) throws TemplateModelException {
+ return NodeModel.wrap(node);
+ }
+
/**
- * Called for an object that isn't considered to be of a "basic" Java type, like for an application specific type,
- * or for a W3C DOM_WRAPPER node. In its default implementation, W3C {@link Node}-s will be wrapped as {@link NodeModel}-s
- * (allows DOM_WRAPPER tree traversal), others will be wrapped using {@link BeansWrapper#wrap(Object)}.
- *
+ * Called for an object that isn't considered to be of a "basic" Java type, like for an application specific type.
+ * In its default implementation, the object will be wrapped as a generic JavaBean.
+ *
* <p>
* When you override this method, you should first decide if you want to wrap the object in a custom way (and if so
* then do it and return with the result), and if not, then you should call the super method (assuming the default
* behavior is fine with you).
*/
protected TemplateModel handleUnknownType(Object obj) throws TemplateModelException {
- if (obj instanceof Node) {
- return wrapDomNode(obj);
+ return modelCache.getInstance(obj);
+ }
+
+ /**
+ * 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 {@link 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 {@link TemplateMethodModelEx} that selects an
+ * overload each time it's called based on the argument values.
+ *
+ * @param object The object whose method will be called, or {@code null} if {@code method} is a static method.
+ * This object will be used "as is", like without unwrapping it if it's a {@link TemplateModelAdapter}.
+ * @param method The method to call, which must be an (inherited) member of the class of {@code object}, as
+ * described by {@link Method#invoke(Object, Object...)}
+ *
+ * @since 2.3.22
+ */
+ public TemplateMethodModelEx wrap(Object object, Method method) {
+ return new SimpleMethodModel(object, method, method.getParameterTypes(), this);
+ }
+
+ /**
+ * @since 2.3.22
+ */
+ @Override
+ public TemplateHashModel wrapAsAPI(Object obj) throws TemplateModelException {
+ return new APIModel(obj, this);
+ }
+
+ private final ModelFactory BOOLEAN_FACTORY = new ModelFactory() {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return ((Boolean) object).booleanValue() ? trueModel : falseModel;
+ }
+ };
+
+ private static final ModelFactory ITERATOR_FACTORY = new ModelFactory() {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new IteratorModel((Iterator<?>) object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+ private static final ModelFactory ENUMERATION_FACTORY = new ModelFactory() {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new EnumerationModel((Enumeration<?>) object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+ protected ModelFactory getModelFactory(Class<?> clazz) {
+ if (Map.class.isAssignableFrom(clazz)) {
+ return SimpleMapModel.FACTORY;
+ }
+ if (Collection.class.isAssignableFrom(clazz)) {
+ return CollectionModel.FACTORY;
+ }
+ if (Number.class.isAssignableFrom(clazz)) {
+ return NumberModel.FACTORY;
+ }
+ if (Date.class.isAssignableFrom(clazz)) {
+ return DateModel.FACTORY;
+ }
+ if (Boolean.class == clazz) { // Boolean is final
+ return BOOLEAN_FACTORY;
+ }
+ if (ResourceBundle.class.isAssignableFrom(clazz)) {
+ return ResourceBundleModel.FACTORY;
+ }
+ if (Iterator.class.isAssignableFrom(clazz)) {
+ return ITERATOR_FACTORY;
+ }
+ if (Enumeration.class.isAssignableFrom(clazz)) {
+ return ENUMERATION_FACTORY;
+ }
+ if (clazz.isArray()) {
+ return ArrayModel.FACTORY;
+ }
+ return StringModel.FACTORY;
+ }
+
+ /**
+ * Attempts to unwrap a model into underlying object. Generally, this
+ * method is the inverse of the {@link #wrap(Object)} method. In addition
+ * it will unwrap arbitrary {@link TemplateNumberModel} instances into
+ * a number, arbitrary {@link TemplateDateModel} instances into a date,
+ * {@link TemplateScalarModel} instances into a String, arbitrary
+ * {@link TemplateBooleanModel} instances into a Boolean, arbitrary
+ * {@link TemplateHashModel} instances into a Map, arbitrary
+ * {@link TemplateSequenceModel} into a List, and arbitrary
+ * {@link TemplateCollectionModel} into a Set. All other objects are
+ * returned unchanged.
+ * @throws TemplateModelException if an attempted unwrapping fails.
+ */
+ @Override
+ public Object unwrap(TemplateModel model) throws TemplateModelException {
+ return unwrap(model, Object.class);
+ }
+
+ /**
+ * Attempts to unwrap a model into an object of the desired class.
+ * Generally, this method is the inverse of the {@link #wrap(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.
+ * @param model the model to unwrap
+ * @param targetClass the class of the unwrapped result; {@code Object.class} of we don't know what the expected type is.
+ * @return the unwrapped result of the desired class
+ * @throws TemplateModelException if an attempted unwrapping fails.
+ *
+ * @see #tryUnwrapTo(TemplateModel, Class)
+ */
+ public Object unwrap(TemplateModel model, Class<?> targetClass)
+ throws TemplateModelException {
+ final Object obj = tryUnwrapTo(model, targetClass);
+ if (obj == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ throw new TemplateModelException("Can not unwrap model of type " +
+ model.getClass().getName() + " to type " + targetClass.getName());
+ }
+ return obj;
+ }
+
+ /**
+ * @since 2.3.22
+ */
+ @Override
+ public Object tryUnwrapTo(TemplateModel model, Class<?> targetClass) throws TemplateModelException {
+ return tryUnwrapTo(model, targetClass, 0);
+ }
+
+ /**
+ * @param typeFlags
+ * Used when unwrapping for overloaded methods and so the {@code targetClass} is possibly too generic
+ * (as it's the most specific common superclass). Must be 0 when unwrapping parameter values for
+ * non-overloaded methods.
+ * @return {@link ObjectWrapperAndUnwrapper#CANT_UNWRAP_TO_TARGET_CLASS} or the unwrapped object.
+ */
+ Object tryUnwrapTo(TemplateModel model, Class<?> targetClass, int typeFlags) throws TemplateModelException {
+ Object res = tryUnwrapTo(model, targetClass, typeFlags, null);
+ if ((typeFlags & TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT) != 0
+ && res instanceof Number) {
+ return OverloadedNumberUtil.addFallbackType((Number) res, typeFlags);
+ } else {
+ return res;
+ }
+ }
+
+ /**
+ * See {@link #tryUnwrapTo(TemplateModel, Class, int)}.
+ */
+ private Object tryUnwrapTo(final TemplateModel model, Class<?> targetClass, final int typeFlags,
+ final Map<Object, Object> recursionStops)
+ throws TemplateModelException {
+ if (model == null) {
+ return null;
+ }
+
+ if (targetClass.isPrimitive()) {
+ targetClass = _ClassUtil.primitiveClassToBoxingClass(targetClass);
+ }
+
+ // This is for transparent interop with other wrappers (and ourselves)
+ // Passing the targetClass allows e.g. a Jython-aware method that declares a
+ // PyObject as its argument to receive a PyObject from a Jython-aware TemplateModel
+ // 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((Number) wrapped, targetClass);
+ if (number != null) return 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((Number) wrapped, targetClass);
+ 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) {
+
+ // [2.4][IcI]: Should also 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 ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
+ }
+
+ // Primitive numeric types & Number.class and its subclasses
+ if (_ClassUtil.isNumerical(targetClass)) {
+ if (model instanceof TemplateNumberModel) {
+ Number number = forceUnwrappedNumberToType(
+ ((TemplateNumberModel) model).getAsNumber(), targetClass);
+ if (number != null) {
+ return number;
+ }
+ }
+ }
+
+ if (boolean.class == targetClass || Boolean.class == targetClass) {
+ if (model instanceof TemplateBooleanModel) {
+ return Boolean.valueOf(((TemplateBooleanModel) model).getAsBoolean());
+ }
+ // Boolean is final, no other conversion will work
+ return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
+ }
+
+ if (Map.class == targetClass) {
+ if (model instanceof TemplateHashModel) {
+ return new HashAdapter((TemplateHashModel) model, this);
+ }
+ }
+
+ if (List.class == targetClass) {
+ if (model instanceof TemplateSequenceModel) {
+ return new SequenceAdapter((TemplateSequenceModel) model, this);
+ }
+ }
+
+ if (Set.class == targetClass) {
+ if (model instanceof TemplateCollectionModel) {
+ return new SetAdapter((TemplateCollectionModel) model, this);
+ }
+ }
+
+ if (Collection.class == targetClass || Iterable.class == targetClass) {
+ if (model instanceof TemplateCollectionModel) {
+ return new CollectionAdapter((TemplateCollectionModel) model,
+ this);
+ }
+ if (model instanceof TemplateSequenceModel) {
+ return new SequenceAdapter((TemplateSequenceModel) model, this);
+ }
+ }
+
+ // TemplateSequenceModels can be converted to arrays
+ if (targetClass.isArray()) {
+ if (model instanceof TemplateSequenceModel) {
+ return unwrapSequenceToArray((TemplateSequenceModel) model, targetClass, true, recursionStops);
+ }
+ // array classes are final, no other conversion will work
+ return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
+ }
+
+ // 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 Character.valueOf(s.charAt(0));
+ }
+ }
+ // Character is final, no other conversion will work
+ return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
+ }
+
+ 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 & TypeFlags.ACCEPTS_NUMBER) != 0)
+ && model instanceof TemplateNumberModel) {
+ Number number = ((TemplateNumberModel) model).getAsNumber();
+ if (itf != 0 || targetClass.isInstance(number)) {
+ return number;
+ }
+ }
+ if ((itf == 0 || (itf & TypeFlags.ACCEPTS_DATE) != 0)
+ && model instanceof TemplateDateModel) {
+ Date date = ((TemplateDateModel) model).getAsDate();
+ if (itf != 0 || targetClass.isInstance(date)) {
+ return date;
+ }
+ }
+ if ((itf == 0 || (itf & (TypeFlags.ACCEPTS_STRING | TypeFlags.CHARACTER)) != 0)
+ && model instanceof TemplateScalarModel
+ && (itf != 0 || targetClass.isAssignableFrom(String.class))) {
+ String strVal = ((TemplateScalarModel) model).getAsString();
+ if (itf == 0 || (itf & TypeFlags.CHARACTER) == 0) {
+ return strVal;
+ } else { // TypeFlags.CHAR == 1
+ if (strVal.length() == 1) {
+ if ((itf & TypeFlags.ACCEPTS_STRING) != 0) {
+ return new CharacterOrString(strVal);
+ } else {
+ return Character.valueOf(strVal.charAt(0));
+ }
+ } else if ((itf & TypeFlags.ACCEPTS_STRING) != 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 & TypeFlags.ACCEPTS_BOOLEAN) != 0)
+ && model instanceof TemplateBooleanModel
+ && (itf != 0 || targetClass.isAssignableFrom(Boolean.class))) {
+ return Boolean.valueOf(((TemplateBooleanModel) model).getAsBoolean());
+ }
+ if ((itf == 0 || (itf & TypeFlags.ACCEPTS_MAP) != 0)
+ && model instanceof TemplateHashModel
+ && (itf != 0 || targetClass.isAssignableFrom(HashAdapter.class))) {
+ return new HashAdapter((TemplateHashModel) model, this);
+ }
+ if ((itf == 0 || (itf & TypeFlags.ACCEPTS_LIST) != 0)
+ && model instanceof TemplateSequenceModel
+ && (itf != 0 || targetClass.isAssignableFrom(SequenceAdapter.class))) {
+ return new SequenceAdapter((TemplateSequenceModel) model, this);
+ }
+ if ((itf == 0 || (itf & TypeFlags.ACCEPTS_SET) != 0)
+ && model instanceof TemplateCollectionModel
+ && (itf != 0 || targetClass.isAssignableFrom(SetAdapter.class))) {
+ return new SetAdapter((TemplateCollectionModel) model, this);
+ }
+
+ if ((itf & TypeFlags.ACCEPTS_ARRAY) != 0
+ && model instanceof TemplateSequenceModel) {
+ return new SequenceAdapter((TemplateSequenceModel) model, this);
+ }
+
+ 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;
+ }
+
+ return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
+ }
+
+ /**
+ * @param tryOnly
+ * If {@code true}, if the conversion of an item to the component type isn't possible, the method returns
+ * {@link ObjectWrapperAndUnwrapper#CANT_UNWRAP_TO_TARGET_CLASS} instead of throwing a
+ * {@link TemplateModelException}.
+ */
+ Object unwrapSequenceToArray(
+ TemplateSequenceModel seq, Class<?> arrayClass, boolean tryOnly, Map<Object, Object> 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(componentType, seq.size());
+ recursionStops.put(seq, array);
+ try {
+ final int size = seq.size();
+ for (int i = 0; i < size; i++) {
+ final TemplateModel seqItem = seq.get(i);
+ Object val = tryUnwrapTo(seqItem, componentType, 0, recursionStops);
+ if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ if (tryOnly) {
+ return ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS;
+ } else {
+ throw new _TemplateModelException(
+ "Failed to convert ", new _DelayedFTLTypeDescription(seq),
+ " object to ", new _DelayedShortClassName(array.getClass()),
+ ": Problematic sequence item at index ", Integer.valueOf(i) ," with value type: ",
+ new _DelayedFTLTypeDescription(seqItem));
+ }
+
+ }
+ Array.set(array, i, val);
+ }
+ } finally {
+ recursionStops.remove(seq);
}
- return super.wrap(obj);
+ return array;
}
-
- public TemplateModel wrapDomNode(Object obj) {
- return NodeModel.wrap((Node) obj);
+
+ Object listToArray(List<?> list, Class<?> arrayClass, Map<Object, Object> recursionStops)
+ throws TemplateModelException {
+ if (list instanceof SequenceAdapter) {
+ return unwrapSequenceToArray(
+ ((SequenceAdapter) list).getTemplateSequenceModel(),
+ arrayClass, false,
+ 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(componentType, list.size());
+ recursionStops.put(list, array);
+ try {
+ boolean isComponentTypeExamined = false;
+ boolean isComponentTypeNumerical = false; // will be filled on demand
+ boolean isComponentTypeList = false; // will be filled on demand
+ int i = 0;
+ for (Object listItem : list) {
+ 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((Number) listItem, componentType);
+ } else if (componentType == String.class && listItem instanceof Character) {
+ listItem = String.valueOf(((Character) listItem).charValue());
+ } else if ((componentType == Character.class || componentType == char.class)
+ && listItem instanceof String) {
+ String listItemStr = (String) listItem;
+ if (listItemStr.length() == 1) {
+ listItem = Character.valueOf(listItemStr.charAt(0));
+ }
+ } else if (componentType.isArray()) {
+ if (listItem instanceof List) {
+ listItem = listToArray((List<?>) listItem, componentType, recursionStops);
+ } else if (listItem instanceof TemplateSequenceModel) {
+ listItem = unwrapSequenceToArray((TemplateSequenceModel) listItem, componentType, false, recursionStops);
+ }
+ } else if (isComponentTypeList && listItem.getClass().isArray()) {
+ listItem = arrayToList(listItem);
+ }
+ }
+ try {
+ Array.set(array, i, listItem);
+ } 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;
}
/**
- * Converts an array to a java.util.List.
+ * @param array Must be an array (of either a reference or primitive type)
*/
- protected Object convertArray(Object arr) {
- // FM 2.4: Use Arrays.asList instead
- final int size = Array.getLength(arr);
- ArrayList list = new ArrayList(size);
- for (int i = 0; i < size; i++) {
- list.add(Array.get(arr, i));
+ List<?> arrayToList(Object array) throws 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 ? Collections.EMPTY_LIST : new NonPrimitiveArrayBackedReadOnlyList(objArray);
+ } else {
+ // Array of any primitive type
+ return Array.getLength(array) == 0 ? Collections.EMPTY_LIST : new PrimtiveArrayBackedReadOnlyList(array);
+ }
+ }
+
+ /**
+ * Converts a number to the target type aggressively (possibly with overflow or significant loss of precision).
+ * @param n Non-{@code null}
+ * @return {@code null} if the conversion has failed.
+ */
+ static Number forceUnwrappedNumberToType(final Number n, final Class<?> targetType) {
+ // 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 ? (Integer) n : Integer.valueOf(n.intValue());
+ } else if (targetType == long.class || targetType == Long.class) {
+ return n instanceof Long ? (Long) n : Long.valueOf(n.longValue());
+ } else if (targetType == double.class || targetType == Double.class) {
+ return n instanceof Double ? (Double) n : Double.valueOf(n.doubleValue());
+ } else if (targetType == BigDecimal.class) {
+ if (n instanceof BigDecimal) {
+ return n;
+ } else if (n instanceof BigInteger) {
+ return new BigDecimal((BigInteger) n);
+ } 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 ? (Float) n : Float.valueOf(n.floatValue());
+ } else if (targetType == byte.class || targetType == Byte.class) {
+ return n instanceof Byte ? (Byte) n : Byte.valueOf(n.byteValue());
+ } else if (targetType == short.class || targetType == Short.class) {
+ return n instanceof Short ? (Short) n : Short.valueOf(n.shortValue());
+ } else if (targetType == BigInteger.class) {
+ if (n instanceof BigInteger) {
+ return n;
+ } else {
+ if (n instanceof OverloadedNumberUtil.IntegerBigDecimal) {
+ return ((OverloadedNumberUtil.IntegerBigDecimal) n).bigIntegerValue();
+ } else if (n instanceof BigDecimal) {
+ return ((BigDecimal) n).toBigInteger();
+ } else {
+ return BigInteger.valueOf(n.longValue());
+ }
+ }
+ } else {
+ final Number oriN = n instanceof OverloadedNumberUtil.NumberWithFallbackType
+ ? ((OverloadedNumberUtil.NumberWithFallbackType) n).getSourceNumber() : n;
+ if (targetType.isInstance(oriN)) {
+ // Handle nonstandard Number subclasses as well as directly java.lang.Number.
+ return oriN;
+ } else {
+ // Fails
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Invokes the specified method, wrapping the return value. The specialty
+ * of this method is that if the return value is null, and the return type
+ * of the invoked method is void, {@link TemplateModel#NOTHING} is returned.
+ * @param object the object to invoke the method on
+ * @param method the method to invoke
+ * @param args the arguments to the method
+ * @return the wrapped return value of the method.
+ * @throws InvocationTargetException if the invoked method threw an exception
+ * @throws IllegalAccessException if the method can't be invoked due to an
+ * access restriction.
+ * @throws TemplateModelException if the return value couldn't be wrapped
+ * (this can happen if the wrapper has an outer identity or is subclassed,
+ * and the outer identity or the subclass throws an exception. Plain
+ * DefaultObjectWrapper never throws TemplateModelException).
+ */
+ TemplateModel invokeMethod(Object object, Method method, Object[] args)
+ throws InvocationTargetException,
+ IllegalAccessException,
+ TemplateModelException {
+ // [2.4]: Java's Method.invoke truncates numbers if the target type has not enough bits to hold the value.
+ // There should at least be an option to check this.
+ Object retval = method.invoke(object, args);
+ return
+ method.getReturnType() == void.class
+ ? TemplateModel.NOTHING
+ : getOuterIdentity().wrap(retval);
+ }
+
+ /**
+ * Returns a hash model that represents the so-called class static models.
+ * Every class static model is itself a hash through which you can call
+ * static methods on the specified class. To obtain a static model for a
+ * class, get the element of this hash with the fully qualified class name.
+ * For example, if you place this hash model inside the root data model
+ * under name "statics", you can use i.e. <code>statics["java.lang.
+ * System"]. currentTimeMillis()</code> to call the {@link
+ * java.lang.System#currentTimeMillis()} method.
+ * @return a hash model whose keys are fully qualified class names, and
+ * that returns hash models whose elements are the static models of the
+ * classes.
+ */
+ public TemplateHashModel getStaticModels() {
+ return staticModels;
+ }
+
+ /**
+ * Returns a hash model that represents the so-called class enum models.
+ * Every class' enum model is itself a hash through which you can access
+ * enum value declared by the specified class, assuming that class is an
+ * enumeration. To obtain an enum model for a class, get the element of this
+ * hash with the fully qualified class name. For example, if you place this
+ * hash model inside the root data model under name "enums", you can use
+ * i.e. <code>statics["java.math.RoundingMode"].UP</code> to access the
+ * {@link java.math.RoundingMode#UP} value.
+ * @return a hash model whose keys are fully qualified class names, and
+ * that returns hash models whose elements are the enum models of the
+ * classes.
+ */
+ public TemplateHashModel getEnumModels() {
+ return enumModels;
+ }
+
+ /** For Unit tests only */
+ ModelCache getModelCache() {
+ return modelCache;
+ }
+
+ /**
+ * Creates a new instance of the specified class using the method call logic of this object wrapper for calling the
+ * constructor. Overloaded constructors and varargs are supported. Only public constructors will be called.
+ *
+ * @param clazz The class whose constructor we will call.
+ * @param arguments The list of {@link TemplateModel}-s to pass to the constructor after unwrapping them
+ * @return The instance created; it's not wrapped into {@link TemplateModel}.
+ */
+ public Object newInstance(Class<?> clazz, List/*<? extends TemplateModel>*/ arguments)
+ throws TemplateModelException {
+ try {
+ Object ctors = classIntrospector.get(clazz).get(ClassIntrospector.CONSTRUCTORS_KEY);
+ if (ctors == null) {
+ throw new TemplateModelException("Class " + clazz.getName() +
+ " has no public constructors.");
+ }
+ Constructor<?> ctor = null;
+ Object[] objargs;
+ if (ctors instanceof SimpleMethod) {
+ SimpleMethod sm = (SimpleMethod) ctors;
+ ctor = (Constructor<?>) sm.getMember();
+ objargs = sm.unwrapArguments(arguments, this);
+ try {
+ return ctor.newInstance(objargs);
+ } catch (Exception e) {
+ if (e instanceof TemplateModelException) throw (TemplateModelException) e;
+ throw _MethodUtil.newInvocationTemplateModelException(null, ctor, e);
+ }
+ } else if (ctors instanceof OverloadedMethods) {
+ final MemberAndArguments mma = ((OverloadedMethods) ctors).getMemberAndArguments(arguments, this);
+ try {
+ return mma.invokeConstructor(this);
+ } catch (Exception e) {
+ if (e instanceof TemplateModelException) throw (TemplateModelException) e;
+
+ throw _MethodUtil.newInvocationTemplateModelException(null, mma.getCallableMemberDescriptor(), e);
+ }
+ } else {
+ // Cannot happen
+ throw new BugException();
+ }
+ } catch (TemplateModelException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new TemplateModelException(
+ "Error while creating new instance of class " + clazz.getName() + "; see cause exception", e);
+ }
+ }
+
+ /**
+ * Removes the introspection data for a class from the cache.
+ * Use this if you know that a class is not used anymore in templates.
+ * If the class will be still used, the cache entry will be silently
+ * re-created, so this isn't a dangerous operation.
+ *
+ * @since 2.3.20
+ */
+ public void removeFromClassIntrospectionCache(Class<?> clazz) {
+ classIntrospector.remove(clazz);
+ }
+
+ /**
+ * Removes all class introspection data from the cache.
+ *
+ * <p>Use this if you want to free up memory on the expense of recreating
+ * the cache entries for the classes that will be used later in templates.
+ *
+ * @throws IllegalStateException if {@link #isClassIntrospectionCacheRestricted()} is {@code true}.
+ *
+ * @since 2.3.20
+ */
+ public void clearClassIntrospecitonCache() {
+ classIntrospector.clearCache();
+ }
+
+ ClassIntrospector getClassIntrospector() {
+ return classIntrospector;
+ }
+
+ /**
+ * Converts any {@link BigDecimal}s in the passed array to the type of
+ * the corresponding formal argument of the method.
+ */
+ // Unused?
+ public static void coerceBigDecimals(AccessibleObject callable, Object[] args) {
+ Class<?>[] formalTypes = null;
+ for (int i = 0; i < args.length; ++i) {
+ Object arg = args[i];
+ if (arg instanceof BigDecimal) {
+ if (formalTypes == null) {
+ if (callable instanceof Method) {
+ formalTypes = ((Method) callable).getParameterTypes();
+ } else if (callable instanceof Constructor) {
+ formalTypes = ((Constructor<?>) callable).getParameterTypes();
+ } else {
+ throw new IllegalArgumentException("Expected method or "
+ + " constructor; callable is " +
+ callable.getClass().getName());
+ }
+ }
+ args[i] = coerceBigDecimal((BigDecimal) arg, formalTypes[i]);
+ }
+ }
+ }
+
+ /**
+ * Converts any {@link BigDecimal}-s in the passed array to the type of
+ * the corresponding formal argument of the method via {@link #coerceBigDecimal(BigDecimal, Class)}.
+ */
+ public static void coerceBigDecimals(Class<?>[] formalTypes, Object[] args) {
+ int typeLen = formalTypes.length;
+ int argsLen = args.length;
+ int min = Math.min(typeLen, argsLen);
+ for (int i = 0; i < min; ++i) {
+ Object arg = args[i];
+ if (arg instanceof BigDecimal) {
+ args[i] = coerceBigDecimal((BigDecimal) arg, formalTypes[i]);
+ }
+ }
+ if (argsLen > typeLen) {
+ Class<?> varArgType = formalTypes[typeLen - 1];
+ for (int i = typeLen; i < argsLen; ++i) {
+ Object arg = args[i];
+ if (arg instanceof BigDecimal) {
+ args[i] = coerceBigDecimal((BigDecimal) arg, varArgType);
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts {@link BigDecimal} to the class given in the {@code formalType} argument if that's a known numerical
+ * type, returns the {@link BigDecimal} as is otherwise. Overflow and precision loss are possible, similarly as
+ * with casting in Java.
+ */
+ public static Object coerceBigDecimal(BigDecimal bd, Class<?> formalType) {
+ // int is expected in most situations, so we check it first
+ if (formalType == int.class || formalType == Integer.class) {
+ return Integer.valueOf(bd.intValue());
+ } else if (formalType == double.class || formalType == Double.class) {
+ return Double.valueOf(bd.doubleValue());
+ } else if (formalType == long.class || formalType == Long.class) {
+ return Long.valueOf(bd.longValue());
+ } else if (formalType == float.class || formalType == Float.class) {
+ return Float.valueOf(bd.floatValue());
+ } else if (formalType == short.class || formalType == Short.class) {
+ return Short.valueOf(bd.shortValue());
+ } else if (formalType == byte.class || formalType == Byte.class) {
+ return Byte.valueOf(bd.byteValue());
+ } else if (java.math.BigInteger.class.isAssignableFrom(formalType)) {
+ return bd.toBigInteger();
+ } else {
+ return bd;
}
- return list;
}
/**
* Returns the lowest version number that is equivalent with the parameter version.
- *
+ *
* @since 2.3.22
*/
protected static Version normalizeIncompatibleImprovementsVersion(Version incompatibleImprovements) {
- return BeansWrapper.normalizeIncompatibleImprovementsVersion(incompatibleImprovements);
+ _CoreAPI.checkVersionNotNullAndSupported(incompatibleImprovements);
+ return Configuration.VERSION_3_0_0;
}
+
/**
- * @since 2.3.22
+ * Returns the name-value pairs that describe the configuration of this {@link DefaultObjectWrapper}; called from
+ * {@link #toString()}. The expected format is like {@code "foo=bar, baaz=wombat"}. When overriding this, you should
+ * call the super method, and then insert the content before it with a following {@code ", "}, or after it with a
+ * preceding {@code ", "}.
*/
- @Override
protected String toPropertiesString() {
- String bwProps = super.toPropertiesString();
-
- // Remove simpleMapWrapper, as its irrelevant for this wrapper:
- if (bwProps.startsWith("simpleMapWrapper")) {
- int smwEnd = bwProps.indexOf(',');
- if (smwEnd != -1) {
- bwProps = bwProps.substring(smwEnd + 1).trim();
- }
- }
-
- return "";
+ // Start with "simpleMapWrapper", because the override in DefaultObjectWrapper expects it to be there!
+ return "exposureLevel=" + classIntrospector.getExposureLevel() + ", "
+ + "exposeFields=" + classIntrospector.getExposeFields() + ", "
+ + "sharedClassIntrospCache="
+ + (classIntrospector.isShared() ? "@" + System.identityHashCode(classIntrospector) : "none");
}
-
+
+ /**
+ * Returns the exact class name and the identity hash, also the values of the most often used
+ * {@link DefaultObjectWrapper} configuration properties, also if which (if any) shared class introspection
+ * cache it uses.
+ */
+ @Override
+ public String toString() {
+ final String propsStr = toPropertiesString();
+ return _ClassUtil.getShortClassNameOfObject(this) + "@" + System.identityHashCode(this)
+ + "(" + incompatibleImprovements + ", "
+ + (propsStr.length() != 0 ? propsStr + ", ..." : "")
+ + ")";
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperBuilder.java b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperBuilder.java
index 40768f8..89af870 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperBuilder.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperBuilder.java
@@ -25,17 +25,85 @@ import java.util.Map;
import java.util.WeakHashMap;
import org.apache.freemarker.core.Version;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder;
-import org.apache.freemarker.core.model.impl.beans._BeansAPI;
+import org.apache.freemarker.core.model.TemplateModel;
/**
- * Gets/creates a {@link DefaultObjectWrapper} singleton instance that's already configured as specified in the
- * properties of this object; this is recommended over using the {@link DefaultObjectWrapper} constructors. The returned
- * instance can't be further configured (it's write protected).
- *
- * <p>See {@link BeansWrapperBuilder} for more info, as that works identically.
- *
- * @since 2.3.21
+ * Gets/creates a {@link DefaultObjectWrapper} singleton instance that's already configured as specified in the properties of
+ * this object; this is recommended over using the {@link DefaultObjectWrapper} constructors. The returned instance can't be
+ * further configured (it's write protected).
+ *
+ * <p>The builder meant to be used as a drop-away object (not stored in a field), like in this example:
+ * <pre>
+ * DefaultObjectWrapper dow = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_21).build();
+ * </pre>
+ *
+ * <p>Or, a more complex example:</p>
+ * <pre>
+ * // Create the builder:
+ * DefaultObjectWrapperBuilder builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_21);
+ * // Set desired DefaultObjectWrapper configuration properties:
+ * builder.setUseModelCache(true);
+ * builder.setExposeFields(true);
+ *
+ * // Get the singleton:
+ * DefaultObjectWrapper dow = builder.build();
+ * // You don't need the builder anymore.
+ * </pre>
+ *
+ * <p>Despite that builders aren't meant to be used as long-lived objects (singletons), the builder is thread-safe after
+ * you have stopped calling its setters and it was safely published (see JSR 133) to other threads. This can be useful
+ * if you have to put the builder into an IoC container, rather than the singleton it produces.
+ *
+ * <p>The main benefit of using a builder instead of a {@link DefaultObjectWrapper} constructor is that this way the
+ * internal object wrapping-related caches (most notably the class introspection cache) will come from a global,
+ * JVM-level (more precisely, {@code freemarker-core.jar}-class-loader-level) cache. Also the
+ * {@link DefaultObjectWrapper} singletons
+ * themselves are stored in this global cache. Some of the wrapping-related caches are expensive to build and can take
+ * significant amount of memory. Using builders, components that use FreeMarker will share {@link DefaultObjectWrapper}
+ * instances and said caches even if they use separate FreeMarker {@link org.apache.freemarker.core.Configuration}-s. (Many Java libraries use
+ * FreeMarker internally, so {@link org.apache.freemarker.core.Configuration} sharing is not an option.)
+ *
+ * <p>Note that the returned {@link DefaultObjectWrapper} instances are only weak-referenced from inside the builder mechanism,
+ * so singletons are garbage collected when they go out of usage, just like non-singletons.
+ *
+ * <p>About the object wrapping-related caches:
+ * <ul>
+ * <li><p>Class introspection cache: Stores information about classes that once had to be wrapped. The cache is
+ * stored in the static fields of certain FreeMarker classes. Thus, if you have two {@link DefaultObjectWrapper}
+ * instances, they might share the same class introspection cache. But if you have two
+ * {@code freemarker.jar}-s (typically, in two Web Application's {@code WEB-INF/lib} directories), those won't
+ * share their caches (as they don't share the same FreeMarker classes).
+ * Also, currently there's a separate cache for each permutation of the property values that influence class
+ * introspection: {@link DefaultObjectWrapperBuilder#setExposeFields(boolean) expose_fields} and
+ * {@link DefaultObjectWrapperBuilder#setExposureLevel(int) exposure_level}. So only {@link DefaultObjectWrapper} where those
+ * properties are the same may share class introspection caches among each other.
+ * </li>
+ * <li><p>Model caches: These are local to a {@link DefaultObjectWrapper}. {@link DefaultObjectWrapperBuilder} returns the same
+ * {@link DefaultObjectWrapper} instance for equivalent properties (unless the existing instance was garbage collected
+ * and thus a new one had to be created), hence these caches will be re-used too. {@link DefaultObjectWrapper} instances
+ * are cached in the static fields of FreeMarker too, but there's a separate cache for each
+ * Thread Context Class Loader, which in a servlet container practically means a separate cache for each Web
+ * Application (each servlet context). (This is like so because for resolving class names to classes FreeMarker
+ * uses the Thread Context Class Loader, so the result of the resolution can be different for different
+ * Thread Context Class Loaders.) The model caches are:
+ * <ul>
+ * <li><p>
+ * Static model caches: These are used by the hash returned by {@link DefaultObjectWrapper#getEnumModels()} and
+ * {@link DefaultObjectWrapper#getStaticModels()}, for caching {@link TemplateModel}-s for the static methods/fields
+ * and Java enums that were accessed through them. To use said hashes, you have to put them
+ * explicitly into the data-model or expose them to the template explicitly otherwise, so in most applications
+ * these caches aren't unused.
+ * </li>
+ * <li><p>
+ * Instance model cache: By default off (see {@link DefaultObjectWrapper#setUseModelCache(boolean)}). Caches the
+ * {@link TemplateModel}-s for all Java objects that were accessed from templates.
+ * </li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * <p>Note that what this method documentation says about {@link DefaultObjectWrapper} also applies to
+ * {@link DefaultObjectWrapperBuilder}.
*/
public class DefaultObjectWrapperBuilder extends DefaultObjectWrapperConfiguration {
@@ -65,12 +133,20 @@ public class DefaultObjectWrapperBuilder extends DefaultObjectWrapperConfigurati
* a singleton that is also in use elsewhere.
*/
public DefaultObjectWrapper build() {
- return _BeansAPI.getBeansWrapperSubclassSingleton(
+ return _ModelAPI.getDefaultObjectWrapperSubclassSingleton(
this, INSTANCE_CACHE, INSTANCE_CACHE_REF_QUEUE, DefaultObjectWrapperFactory.INSTANCE);
}
-
+
+ /**
+ * For unit testing only
+ */
+ static Map<ClassLoader, Map<DefaultObjectWrapperConfiguration, WeakReference<DefaultObjectWrapper>>>
+ getInstanceCache() {
+ return INSTANCE_CACHE;
+ }
+
private static class DefaultObjectWrapperFactory
- implements _BeansAPI._BeansWrapperSubclassFactory<DefaultObjectWrapper, DefaultObjectWrapperConfiguration> {
+ implements _ModelAPI._DefaultObjectWrapperSubclassFactory<DefaultObjectWrapper, DefaultObjectWrapperConfiguration> {
private static final DefaultObjectWrapperFactory INSTANCE = new DefaultObjectWrapperFactory();
[17/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java
index 307d278..a2ca780 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperConfiguration.java
@@ -20,8 +20,9 @@
package org.apache.freemarker.core.model.impl;
import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core._CoreAPI;
import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapperConfiguration;
+import org.apache.freemarker.core.model.TemplateDateModel;
/**
* Holds {@link DefaultObjectWrapper} configuration settings and defines their defaults.
@@ -29,13 +30,187 @@ import org.apache.freemarker.core.model.impl.beans.BeansWrapperConfiguration;
* Unless, you are developing a builder for a custom {@link DefaultObjectWrapper} subclass. In that case, note that
* overriding the {@link #equals} and {@link #hashCode} is important, as these objects are used as {@link ObjectWrapper}
* singleton lookup keys.
- *
- * @since 2.3.22
*/
-public abstract class DefaultObjectWrapperConfiguration extends BeansWrapperConfiguration {
-
+public abstract class DefaultObjectWrapperConfiguration implements Cloneable {
+
+ private final Version incompatibleImprovements;
+
+ protected ClassIntrospectorBuilder classIntrospectorFactory;
+
+ // Properties and their *defaults*:
+ private boolean simpleMapWrapper = false;
+ private int defaultDateType = TemplateDateModel.UNKNOWN;
+ private ObjectWrapper outerIdentity = null;
+ private boolean strict = false;
+ private boolean useModelCache = false;
+ // Attention!
+ // - As this object is a cache key, non-normalized field values should be avoided.
+ // - Fields with default values must be set until the end of the constructor to ensure that when the lookup happens,
+ // there will be no unset fields.
+ // - If you add a new field, review all methods in this class
+
+ /**
+ * @param incompatibleImprovements
+ * See the corresponding parameter of {@link DefaultObjectWrapper#DefaultObjectWrapper(Version)}. Not {@code null}. Note
+ * that the version will be normalized to the lowest version where the same incompatible
+ * {@link DefaultObjectWrapper} improvements were already present, so for the returned instance
+ * {@link #getIncompatibleImprovements()} might returns a lower version than what you have specified
+ * here.
+ * @param isIncompImprsAlreadyNormalized
+ * Tells if the {@code incompatibleImprovements} parameter contains an <em>already normalized</em> value.
+ * This parameter meant to be {@code true} when the class that extends {@link DefaultObjectWrapper} needs to add
+ * additional breaking versions over those of {@link DefaultObjectWrapper}. Thus, if this parameter is
+ * {@code true}, the versions where {@link DefaultObjectWrapper} had breaking changes must be already factored
+ * into the {@code incompatibleImprovements} parameter value, as no more normalization will happen. (You
+ * can use {@link DefaultObjectWrapper#normalizeIncompatibleImprovementsVersion(Version)} to discover those.)
+ *
+ * @since 2.3.22
+ */
+ protected DefaultObjectWrapperConfiguration(Version incompatibleImprovements, boolean isIncompImprsAlreadyNormalized) {
+ _CoreAPI.checkVersionNotNullAndSupported(incompatibleImprovements);
+
+ incompatibleImprovements = isIncompImprsAlreadyNormalized
+ ? incompatibleImprovements
+ : DefaultObjectWrapper.normalizeIncompatibleImprovementsVersion(incompatibleImprovements);
+ this.incompatibleImprovements = incompatibleImprovements;
+
+ classIntrospectorFactory = new ClassIntrospectorBuilder(incompatibleImprovements);
+ }
+
+ /**
+ * Same as {@link #DefaultObjectWrapperConfiguration(Version, boolean) DefaultObjectWrapperConfiguration(Version, false)}.
+ */
protected DefaultObjectWrapperConfiguration(Version incompatibleImprovements) {
- super(DefaultObjectWrapper.normalizeIncompatibleImprovementsVersion(incompatibleImprovements), true);
+ this(incompatibleImprovements, false);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + incompatibleImprovements.hashCode();
+ result = prime * result + (simpleMapWrapper ? 1231 : 1237);
+ result = prime * result + defaultDateType;
+ result = prime * result + (outerIdentity != null ? outerIdentity.hashCode() : 0);
+ result = prime * result + (strict ? 1231 : 1237);
+ result = prime * result + (useModelCache ? 1231 : 1237);
+ result = prime * result + classIntrospectorFactory.hashCode();
+ return result;
+ }
+
+ /**
+ * Two {@link DefaultObjectWrapperConfiguration}-s are equal exactly if their classes are identical ({@code ==}), and their
+ * field values are equal.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ DefaultObjectWrapperConfiguration other = (DefaultObjectWrapperConfiguration) obj;
+
+ if (!incompatibleImprovements.equals(other.incompatibleImprovements)) return false;
+ if (simpleMapWrapper != other.simpleMapWrapper) return false;
+ if (defaultDateType != other.defaultDateType) return false;
+ if (outerIdentity != other.outerIdentity) return false;
+ if (strict != other.strict) return false;
+ if (useModelCache != other.useModelCache) return false;
+ return classIntrospectorFactory.equals(other.classIntrospectorFactory);
+ }
+
+ protected Object clone(boolean deepCloneKey) {
+ try {
+ DefaultObjectWrapperConfiguration clone = (DefaultObjectWrapperConfiguration) super.clone();
+ if (deepCloneKey) {
+ clone.classIntrospectorFactory
+ = (ClassIntrospectorBuilder) classIntrospectorFactory.clone();
+ }
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException("Failed to clone DefaultObjectWrapperConfiguration", e);
+ }
+ }
+
+ public int getDefaultDateType() {
+ return defaultDateType;
+ }
+
+ /** See {@link DefaultObjectWrapper#setDefaultDateType(int)}. */
+ public void setDefaultDateType(int defaultDateType) {
+ this.defaultDateType = defaultDateType;
+ }
+
+ public ObjectWrapper getOuterIdentity() {
+ return outerIdentity;
+ }
+
+ /**
+ * See {@link DefaultObjectWrapper#setOuterIdentity(ObjectWrapper)}, except here the default is {@code null} that means
+ * the {@link ObjectWrapper} that you will set up with this {@link DefaultObjectWrapperBuilder} object.
+ */
+ public void setOuterIdentity(ObjectWrapper outerIdentity) {
+ this.outerIdentity = outerIdentity;
+ }
+
+ public boolean isStrict() {
+ return strict;
+ }
+
+ /** See {@link DefaultObjectWrapper#setStrict(boolean)}. */
+ public void setStrict(boolean strict) {
+ this.strict = strict;
+ }
+
+ public boolean getUseModelCache() {
+ return useModelCache;
+ }
+
+ /** See {@link DefaultObjectWrapper#setUseModelCache(boolean)} (it means the same). */
+ public void setUseModelCache(boolean useModelCache) {
+ this.useModelCache = useModelCache;
+ }
+
+ public Version getIncompatibleImprovements() {
+ return incompatibleImprovements;
+ }
+
+ public int getExposureLevel() {
+ return classIntrospectorFactory.getExposureLevel();
+ }
+
+ /** See {@link DefaultObjectWrapper#setExposureLevel(int)}. */
+ public void setExposureLevel(int exposureLevel) {
+ classIntrospectorFactory.setExposureLevel(exposureLevel);
+ }
+
+ public boolean getExposeFields() {
+ return classIntrospectorFactory.getExposeFields();
+ }
+
+ /** See {@link DefaultObjectWrapper#setExposeFields(boolean)}. */
+ public void setExposeFields(boolean exposeFields) {
+ classIntrospectorFactory.setExposeFields(exposeFields);
+ }
+
+ public MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
+ return classIntrospectorFactory.getMethodAppearanceFineTuner();
+ }
+
+ /**
+ * See {@link DefaultObjectWrapper#setMethodAppearanceFineTuner(MethodAppearanceFineTuner)}; additionally,
+ * note that currently setting this to non-{@code null} will disable class introspection cache sharing, unless
+ * the value implements {@link SingletonCustomizer}.
+ */
+ public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner methodAppearanceFineTuner) {
+ classIntrospectorFactory.setMethodAppearanceFineTuner(methodAppearanceFineTuner);
+ }
+
+ MethodSorter getMethodSorter() {
+ return classIntrospectorFactory.getMethodSorter();
+ }
+
+ void setMethodSorter(MethodSorter methodSorter) {
+ classIntrospectorFactory.setMethodSorter(methodSorter);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/EmptyCallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/EmptyCallableMemberDescriptor.java b/src/main/java/org/apache/freemarker/core/model/impl/EmptyCallableMemberDescriptor.java
new file mode 100644
index 0000000..3ec8e87
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/EmptyCallableMemberDescriptor.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * Represents that no member was chosen. Why it wasn't is represented by the two singleton instances,
+ * {@link #NO_SUCH_METHOD} and {@link #AMBIGUOUS_METHOD}. (Note that instances of these are cached associated with the
+ * argument types, thus it shouldn't store details that are specific to the actual argument values. In fact, it better
+ * remains a set of singletons.)
+ */
+final class EmptyCallableMemberDescriptor extends MaybeEmptyCallableMemberDescriptor {
+
+ static final EmptyCallableMemberDescriptor NO_SUCH_METHOD = new EmptyCallableMemberDescriptor();
+ static final EmptyCallableMemberDescriptor AMBIGUOUS_METHOD = new EmptyCallableMemberDescriptor();
+
+ private EmptyCallableMemberDescriptor() { }
+
+}
\ 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/EmptyMemberAndArguments.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/EmptyMemberAndArguments.java b/src/main/java/org/apache/freemarker/core/model/impl/EmptyMemberAndArguments.java
new file mode 100644
index 0000000..25987d7
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/EmptyMemberAndArguments.java
@@ -0,0 +1,93 @@
+/*
+ * 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._DelayedOrdinal;
+
+/**
+ * Describes a failed member lookup. Instances of this must not be cached as instances may store the actual argument
+ * values.
+ */
+final class EmptyMemberAndArguments extends MaybeEmptyMemberAndArguments {
+
+ static final EmptyMemberAndArguments WRONG_NUMBER_OF_ARGUMENTS
+ = new EmptyMemberAndArguments(
+ "No compatible overloaded variation was found; wrong number of arguments.", true, null);
+
+ private final Object errorDescription;
+ private final boolean numberOfArgumentsWrong;
+ private final Object[] unwrappedArguments;
+
+ private EmptyMemberAndArguments(
+ Object errorDescription, boolean numberOfArgumentsWrong, Object[] unwrappedArguments) {
+ this.errorDescription = errorDescription;
+ this.numberOfArgumentsWrong = numberOfArgumentsWrong;
+ this.unwrappedArguments = unwrappedArguments;
+ }
+
+ static EmptyMemberAndArguments noCompatibleOverload(int unwrappableIndex) {
+ return new EmptyMemberAndArguments(
+ new Object[] { "No compatible overloaded variation was found; can't convert (unwrap) the ",
+ new _DelayedOrdinal(Integer.valueOf(unwrappableIndex)), " argument to the desired Java type." },
+ false,
+ null);
+ }
+
+ static EmptyMemberAndArguments noCompatibleOverload(Object[] unwrappedArgs) {
+ return new EmptyMemberAndArguments(
+ "No compatible overloaded variation was found; declared parameter types and argument value types mismatch.",
+ false,
+ unwrappedArgs);
+ }
+
+ static EmptyMemberAndArguments ambiguous(Object[] unwrappedArgs) {
+ return new EmptyMemberAndArguments(
+ "Multiple compatible overloaded variations were found with the same priority.",
+ false,
+ unwrappedArgs);
+ }
+
+ static MaybeEmptyMemberAndArguments from(
+ EmptyCallableMemberDescriptor emtpyMemberDesc, Object[] unwrappedArgs) {
+ if (emtpyMemberDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) {
+ return noCompatibleOverload(unwrappedArgs);
+ } else if (emtpyMemberDesc == EmptyCallableMemberDescriptor.AMBIGUOUS_METHOD) {
+ return ambiguous(unwrappedArgs);
+ } else {
+ throw new IllegalArgumentException("Unrecognized constant: " + emtpyMemberDesc);
+ }
+ }
+
+ Object getErrorDescription() {
+ return errorDescription;
+ }
+
+ /**
+ * @return {@code null} if the error has occurred earlier than the full argument list was unwrapped.
+ */
+ Object[] getUnwrappedArguments() {
+ return unwrappedArguments;
+ }
+
+ public boolean isNumberOfArgumentsWrong() {
+ return numberOfArgumentsWrong;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/EnumerationModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/EnumerationModel.java b/src/main/java/org/apache/freemarker/core/model/impl/EnumerationModel.java
new file mode 100644
index 0000000..d7b14cc
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/EnumerationModel.java
@@ -0,0 +1,108 @@
+/*
+ * 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.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+
+/**
+ * <p>A class that adds {@link TemplateModelIterator} functionality to the
+ * {@link Enumeration} interface implementers.
+ * </p> <p>Using the model as a collection model is NOT thread-safe, as
+ * enumerations are inherently not thread-safe.
+ * Further, you can iterate over it only once. Attempts to call the
+ * {@link #iterator()} method after it was already driven to the end once will
+ * throw an exception.</p>
+ */
+
+public class EnumerationModel
+extends
+ BeanModel
+implements
+ TemplateModelIterator,
+ TemplateCollectionModel {
+ private boolean accessed = false;
+
+ /**
+ * Creates a new model that wraps the specified enumeration object.
+ * @param enumeration the enumeration object to wrap into a model.
+ * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+ * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+ * model gains many attributes from its wrapper, including the caching
+ * behavior, method exposure level, method-over-item shadowing policy etc.
+ */
+ public EnumerationModel(Enumeration enumeration, DefaultObjectWrapper wrapper) {
+ super(enumeration, wrapper);
+ }
+
+ /**
+ * This allows the enumeration to be used in a <tt><#list></tt> block.
+ * @return "this"
+ */
+ @Override
+ public TemplateModelIterator iterator() throws TemplateModelException {
+ synchronized (this) {
+ if (accessed) {
+ throw new TemplateModelException(
+ "This collection is stateful and can not be iterated over the" +
+ " second time.");
+ }
+ accessed = true;
+ }
+ return this;
+ }
+
+ /**
+ * Calls underlying {@link Enumeration#nextElement()}.
+ */
+ @Override
+ public boolean hasNext() {
+ return ((Enumeration) object).hasMoreElements();
+ }
+
+
+ /**
+ * Calls underlying {@link Enumeration#nextElement()} and wraps the result.
+ */
+ @Override
+ public TemplateModel next()
+ throws TemplateModelException {
+ try {
+ return wrap(((Enumeration) object).nextElement());
+ } catch (NoSuchElementException e) {
+ throw new TemplateModelException(
+ "No more elements in the enumeration.");
+ }
+ }
+
+ /**
+ * Returns {@link Enumeration#hasMoreElements()}. Therefore, an
+ * enumeration that has no more element evaluates to false, and an
+ * enumeration that has further elements evaluates to true.
+ */
+ public boolean getAsBoolean() {
+ return hasNext();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
new file mode 100644
index 0000000..7fefec9
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
@@ -0,0 +1,180 @@
+/*
+ * 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.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+
+/**
+ */
+public class HashAdapter extends AbstractMap implements TemplateModelAdapter {
+ private final DefaultObjectWrapper wrapper;
+ private final TemplateHashModel model;
+ private Set entrySet;
+
+ HashAdapter(TemplateHashModel model, DefaultObjectWrapper wrapper) {
+ this.model = model;
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public TemplateModel getTemplateModel() {
+ return model;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ try {
+ return model.isEmpty();
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public Object get(Object key) {
+ try {
+ return wrapper.unwrap(model.get(String.valueOf(key)));
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ // A quick check that doesn't require TemplateHashModelEx
+ if (get(key) != null) {
+ return true;
+ }
+ return super.containsKey(key);
+ }
+
+ @Override
+ public Set entrySet() {
+ if (entrySet != null) {
+ return entrySet;
+ }
+ return entrySet = new AbstractSet() {
+ @Override
+ public Iterator iterator() {
+ final TemplateModelIterator i;
+ try {
+ i = getModelEx().keys().iterator();
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ return new Iterator() {
+ @Override
+ public boolean hasNext() {
+ try {
+ return i.hasNext();
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public Object next() {
+ final Object key;
+ try {
+ key = wrapper.unwrap(i.next());
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ return new Map.Entry() {
+ @Override
+ public Object getKey() {
+ return key;
+ }
+
+ @Override
+ public Object getValue() {
+ return get(key);
+ }
+
+ @Override
+ public Object setValue(Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry e = (Map.Entry) o;
+ Object k1 = getKey();
+ Object k2 = e.getKey();
+ if (k1 == k2 || (k1 != null && k1.equals(k2))) {
+ Object v1 = getValue();
+ Object v2 = e.getValue();
+ if (v1 == v2 || (v1 != null && v1.equals(v2)))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ Object value = getValue();
+ return (key == null ? 0 : key.hashCode()) ^
+ (value == null ? 0 : value.hashCode());
+ }
+ };
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ try {
+ return getModelEx().size();
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+ };
+ }
+
+ private TemplateHashModelEx getModelEx() {
+ if (model instanceof TemplateHashModelEx) {
+ return ((TemplateHashModelEx) model);
+ }
+ throw new UnsupportedOperationException(
+ "Operation supported only on TemplateHashModelEx. " +
+ model.getClass().getName() + " does not implement it though.");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/InvalidPropertyException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/InvalidPropertyException.java b/src/main/java/org/apache/freemarker/core/model/impl/InvalidPropertyException.java
new file mode 100644
index 0000000..41197ca
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/InvalidPropertyException.java
@@ -0,0 +1,34 @@
+/*
+ * 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.model.TemplateModelException;
+
+/**
+ * An exception thrown when there is an attempt to access
+ * an invalid bean property when we are in a "strict bean" mode
+ */
+
+public class InvalidPropertyException extends TemplateModelException {
+
+ public InvalidPropertyException(String description) {
+ super(description);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/IteratorModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/IteratorModel.java b/src/main/java/org/apache/freemarker/core/model/impl/IteratorModel.java
new file mode 100644
index 0000000..0eabf21
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/IteratorModel.java
@@ -0,0 +1,112 @@
+/*
+ * 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.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+
+/**
+ * <p>A class that adds {@link TemplateModelIterator} functionality to the
+ * {@link Iterator} interface implementers.
+ * </p>
+ * <p>It differs from the {@link org.apache.freemarker.core.model.impl.SimpleCollection} in that
+ * it inherits from {@link BeanModel}, and therefore you can call methods on
+ * it directly, even to the effect of calling <tt>iterator.remove()</tt> in
+ * the template.</p> <p>Using the model as a collection model is NOT
+ * thread-safe, as iterators are inherently not thread-safe.
+ * Further, you can iterate over it only once. Attempts to call the
+ * {@link #iterator()} method after it was already driven to the end once will
+ * throw an exception.</p>
+ */
+
+public class IteratorModel
+extends
+ BeanModel
+implements
+ TemplateModelIterator,
+ TemplateCollectionModel {
+ private boolean accessed = false;
+
+ /**
+ * Creates a new model that wraps the specified iterator object.
+ * @param iterator the iterator object to wrap into a model.
+ * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+ * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+ * model gains many attributes from its wrapper, including the caching
+ * behavior, method exposure level, method-over-item shadowing policy etc.
+ */
+ public IteratorModel(Iterator iterator, DefaultObjectWrapper wrapper) {
+ super(iterator, wrapper);
+ }
+
+ /**
+ * This allows the iterator to be used in a <tt><#list></tt> block.
+ * @return "this"
+ */
+ @Override
+ public TemplateModelIterator iterator() throws TemplateModelException {
+ synchronized (this) {
+ if (accessed) {
+ throw new TemplateModelException(
+ "This collection is stateful and can not be iterated over the" +
+ " second time.");
+ }
+ accessed = true;
+ }
+ return this;
+ }
+
+ /**
+ * Calls underlying {@link Iterator#hasNext()}.
+ */
+ @Override
+ public boolean hasNext() {
+ return ((Iterator) object).hasNext();
+ }
+
+
+ /**
+ * Calls underlying {@link Iterator#next()} and wraps the result.
+ */
+ @Override
+ public TemplateModel next()
+ throws TemplateModelException {
+ try {
+ return wrap(((Iterator) object).next());
+ } catch (NoSuchElementException e) {
+ throw new TemplateModelException(
+ "No more elements in the iterator.", e);
+ }
+ }
+
+ /**
+ * Returns {@link Iterator#hasNext()}. Therefore, an
+ * iterator that has no more element evaluates to false, and an
+ * iterator that has further elements evaluates to true.
+ */
+ public boolean getAsBoolean() {
+ return hasNext();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/JRebelClassChangeNotifier.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/JRebelClassChangeNotifier.java b/src/main/java/org/apache/freemarker/core/model/impl/JRebelClassChangeNotifier.java
new file mode 100644
index 0000000..5f8e222
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/JRebelClassChangeNotifier.java
@@ -0,0 +1,58 @@
+/*
+ * 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.lang.ref.WeakReference;
+
+import org.zeroturnaround.javarebel.ClassEventListener;
+import org.zeroturnaround.javarebel.ReloaderFactory;
+
+class JRebelClassChangeNotifier implements ClassChangeNotifier {
+
+ static void testAvailability() {
+ ReloaderFactory.getInstance();
+ }
+
+ @Override
+ public void subscribe(ClassIntrospector classIntrospector) {
+ ReloaderFactory.getInstance().addClassReloadListener(
+ new ClassIntrospectorCacheInvalidator(classIntrospector));
+ }
+
+ private static class ClassIntrospectorCacheInvalidator
+ implements ClassEventListener {
+ private final WeakReference ref;
+
+ ClassIntrospectorCacheInvalidator(ClassIntrospector w) {
+ ref = new WeakReference(w);
+ }
+
+ @Override
+ public void onClassEvent(int eventType, Class pClass) {
+ ClassIntrospector ci = (ClassIntrospector) ref.get();
+ if (ci == null) {
+ ReloaderFactory.getInstance().removeClassReloadListener(this);
+ } else if (eventType == ClassEventListener.EVENT_RELOADED) {
+ ci.remove(pClass);
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/MapModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/MapModel.java b/src/main/java/org/apache/freemarker/core/model/impl/MapModel.java
new file mode 100644
index 0000000..95abb2a
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/MapModel.java
@@ -0,0 +1,120 @@
+/*
+ * 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.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * <p>A special case of {@link BeanModel} that adds implementation
+ * for {@link TemplateMethodModelEx} on map objects that is a shortcut for the
+ * <tt>Map.get()</tt> method. Note that if the passed argument itself is a
+ * reflection-wrapper model, then the map lookup will be performed using the
+ * wrapped object as the key. Note that you can call <tt>get()</tt> using the
+ * <tt>map.key</tt> syntax inherited from {@link BeanModel} as well,
+ * however in that case the key is always a string.</p>
+ * <p>The class itself does not implement the {@link org.apache.freemarker.core.model.TemplateCollectionModel}.
+ * You can, however use <tt>map.entrySet()</tt>, <tt>map.keySet()</tt>, or
+ * <tt>map.values()</tt> to obtain {@link org.apache.freemarker.core.model.TemplateCollectionModel} instances for
+ * various aspects of the map.</p>
+ */
+public class MapModel
+extends
+ StringModel
+implements
+ TemplateMethodModelEx {
+ static final ModelFactory FACTORY =
+ new ModelFactory()
+ {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new MapModel((Map) object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+
+ /**
+ * Creates a new model that wraps the specified map object.
+ * @param map the map object to wrap into a model.
+ * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+ * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+ * model gains many attributes from its wrapper, including the caching
+ * behavior, method exposure level, method-over-item shadowing policy etc.
+ */
+ public MapModel(Map map, DefaultObjectWrapper wrapper) {
+ super(map, wrapper);
+ }
+
+ /**
+ * The first argument is used as a key to call the map's <tt>get</tt> method.
+ */
+ @Override
+ public Object exec(List arguments)
+ throws TemplateModelException {
+ Object key = unwrap((TemplateModel) arguments.get(0));
+ return wrap(((Map) object).get(key));
+ }
+
+ /**
+ * Overridden to invoke the generic get method by casting to Map instead of
+ * through reflection - should yield better performance.
+ */
+ @Override
+ protected TemplateModel invokeGenericGet(Map keyMap, Class clazz, String key)
+ throws TemplateModelException {
+ Map map = (Map) object;
+ Object val = map.get(key);
+ if (val == null) {
+ if (key.length() == 1) {
+ // just check for Character key if this is a single-character string
+ Character charKey = Character.valueOf(key.charAt(0));
+ val = map.get(charKey);
+ if (val == null && !(map.containsKey(key) || map.containsKey(charKey))) {
+ return UNKNOWN;
+ }
+ } else if (!map.containsKey(key)) {
+ return UNKNOWN;
+ }
+ }
+ return wrap(val);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return ((Map) object).isEmpty() && super.isEmpty();
+ }
+
+ @Override
+ public int size() {
+ return keySet().size();
+ }
+
+ @Override
+ protected Set keySet() {
+ Set set = super.keySet();
+ set.addAll(((Map) object).keySet());
+ return set;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/MaybeEmptyCallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/MaybeEmptyCallableMemberDescriptor.java b/src/main/java/org/apache/freemarker/core/model/impl/MaybeEmptyCallableMemberDescriptor.java
new file mode 100644
index 0000000..1b62bf8
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/MaybeEmptyCallableMemberDescriptor.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Superclass of the {@link EmptyCallableMemberDescriptor} and {@link CallableMemberDescriptor} "case classes".
+ */
+abstract class MaybeEmptyCallableMemberDescriptor { }
\ 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/MaybeEmptyMemberAndArguments.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/MaybeEmptyMemberAndArguments.java b/src/main/java/org/apache/freemarker/core/model/impl/MaybeEmptyMemberAndArguments.java
new file mode 100644
index 0000000..bfc88ee
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/MaybeEmptyMemberAndArguments.java
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+abstract class MaybeEmptyMemberAndArguments { }
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/MemberAndArguments.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/MemberAndArguments.java b/src/main/java/org/apache/freemarker/core/model/impl/MemberAndArguments.java
new file mode 100644
index 0000000..e7c735c
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/MemberAndArguments.java
@@ -0,0 +1,64 @@
+/*
+ * 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.lang.reflect.InvocationTargetException;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ */
+class MemberAndArguments extends MaybeEmptyMemberAndArguments {
+
+ private final CallableMemberDescriptor callableMemberDesc;
+ private final Object[] args;
+
+ /**
+ * @param args The already unwrapped arguments
+ */
+ MemberAndArguments(CallableMemberDescriptor callableMemberDesc, Object[] args) {
+ this.callableMemberDesc = callableMemberDesc;
+ this.args = args;
+ }
+
+ /**
+ * The already unwrapped arguments.
+ */
+ Object[] getArgs() {
+ return args;
+ }
+
+ TemplateModel invokeMethod(DefaultObjectWrapper ow, Object obj)
+ throws TemplateModelException, InvocationTargetException, IllegalAccessException {
+ return callableMemberDesc.invokeMethod(ow, obj, args);
+ }
+
+ Object invokeConstructor(DefaultObjectWrapper ow)
+ throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException,
+ TemplateModelException {
+ return callableMemberDesc.invokeConstructor(ow, args);
+ }
+
+ CallableMemberDescriptor getCallableMemberDescriptor() {
+ return callableMemberDesc;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/MethodAppearanceFineTuner.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/MethodAppearanceFineTuner.java b/src/main/java/org/apache/freemarker/core/model/impl/MethodAppearanceFineTuner.java
new file mode 100644
index 0000000..3235c51
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/MethodAppearanceFineTuner.java
@@ -0,0 +1,156 @@
+/*
+ * 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.IndexedPropertyDescriptor;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+
+/**
+ * Used for customizing how the methods are visible from templates, via
+ * {@link DefaultObjectWrapper#setMethodAppearanceFineTuner(MethodAppearanceFineTuner)}.
+ * The object that implements this should also implement {@link SingletonCustomizer} whenever possible.
+ *
+ * @since 2.3.21
+ */
+public interface MethodAppearanceFineTuner {
+
+ /**
+ * Implement this to tweak certain aspects of how methods appear in the
+ * data-model. {@link DefaultObjectWrapper} will pass in all Java methods here that
+ * it intends to expose in the data-model as methods (so you can do
+ * <tt>obj.foo()</tt> in the template).
+ * With this method you can do the following tweaks:
+ * <ul>
+ * <li>Hide a method that would be otherwise shown by calling
+ * {@link org.apache.freemarker.core.model.impl.MethodAppearanceFineTuner.Decision#setExposeMethodAs(String)}
+ * with <tt>null</tt> parameter. Note that you can't un-hide methods
+ * that are not public or are considered to by unsafe
+ * (like {@link Object#wait()}) because
+ * {@link #process} is not called for those.</li>
+ * <li>Show the method with a different name in the data-model than its
+ * real name by calling
+ * {@link org.apache.freemarker.core.model.impl.MethodAppearanceFineTuner.Decision#setExposeMethodAs(String)}
+ * with non-<tt>null</tt> parameter.
+ * <li>Create a fake JavaBean property for this method by calling
+ * {@link org.apache.freemarker.core.model.impl.MethodAppearanceFineTuner.Decision#setExposeAsProperty(PropertyDescriptor)}.
+ * For example, if you have <tt>int size()</tt> in a class, but you
+ * want it to be accessed from the templates as <tt>obj.size</tt>,
+ * rather than as <tt>obj.size()</tt>, you can do that with this.
+ * The default is {@code null}, which means that no fake property is
+ * created for the method. You need not and shouldn't set this
+ * to non-<tt>null</tt> for the getter methods of real JavaBean
+ * properties, as those are automatically shown as properties anyway.
+ * The property name in the {@link PropertyDescriptor} can be anything,
+ * but the method (or methods) in it must belong to the class that
+ * is given as the <tt>clazz</tt> parameter or it must be inherited from
+ * that class, or else whatever errors can occur later.
+ * {@link IndexedPropertyDescriptor}-s are supported.
+ * If a real JavaBean property of the same name exists, it won't be
+ * replaced by the fake one. Also if a fake property of the same name
+ * was assigned earlier, it won't be replaced.
+ * <li>Prevent the method to hide a JavaBean property (fake or real) of
+ * the same name by calling
+ * {@link org.apache.freemarker.core.model.impl.MethodAppearanceFineTuner.Decision#setMethodShadowsProperty(boolean)}
+ * with <tt>false</tt>. The default is <tt>true</tt>, so if you have
+ * both a property and a method called "foo", then in the template
+ * <tt>myObject.foo</tt> will return the method itself instead
+ * of the property value, which is often undesirable.
+ * </ul>
+ *
+ * <p>Note that you can expose a Java method both as a method and as a
+ * JavaBean property on the same time, however you have to chose different
+ * names for them to prevent shadowing.
+ *
+ * @param in Describes the method about which the decision will have to be made.
+ *
+ * @param out Stores how the method will be exposed in the
+ * data-model after {@link #process} returns.
+ * This is initialized so that it reflects the default
+ * behavior of {@link DefaultObjectWrapper}, so you don't have to do anything with this
+ * when you don't want to change the default behavior.
+ */
+ void process(DecisionInput in, Decision out);
+
+ /**
+ * Used for {@link MethodAppearanceFineTuner#process} to store the results.
+ */
+ final class Decision {
+ private PropertyDescriptor exposeAsProperty;
+ private String exposeMethodAs;
+ private boolean methodShadowsProperty;
+
+ void setDefaults(Method m) {
+ exposeAsProperty = null;
+ exposeMethodAs = m.getName();
+ methodShadowsProperty = true;
+ }
+
+ public PropertyDescriptor getExposeAsProperty() {
+ return exposeAsProperty;
+ }
+
+ public void setExposeAsProperty(PropertyDescriptor exposeAsProperty) {
+ this.exposeAsProperty = exposeAsProperty;
+ }
+
+ public String getExposeMethodAs() {
+ return exposeMethodAs;
+ }
+
+ public void setExposeMethodAs(String exposeMethodAs) {
+ this.exposeMethodAs = exposeMethodAs;
+ }
+
+ public boolean getMethodShadowsProperty() {
+ return methodShadowsProperty;
+ }
+
+ public void setMethodShadowsProperty(boolean methodShadowsProperty) {
+ this.methodShadowsProperty = methodShadowsProperty;
+ }
+
+ }
+
+ /**
+ * Used for {@link org.apache.freemarker.core.model.impl.MethodAppearanceFineTuner#process} as input parameter.
+ */
+ final class DecisionInput {
+ private Method method;
+ private Class<?> containingClass;
+
+ void setMethod(Method method) {
+ this.method = method;
+ }
+
+ void setContainingClass(Class<?> containingClass) {
+ this.containingClass = containingClass;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public Class/*<?>*/ getContainingClass() {
+ return containingClass;
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/MethodSorter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/MethodSorter.java b/src/main/java/org/apache/freemarker/core/model/impl/MethodSorter.java
new file mode 100644
index 0000000..b893bfe
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/MethodSorter.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Used for JUnit testing method-order dependence bugs via
+ * {@link DefaultObjectWrapperBuilder#setMethodSorter(MethodSorter)}.
+ */
+interface MethodSorter {
+
+ MethodDescriptor[] sortMethodDescriptors(MethodDescriptor[] methodDescriptors);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/ModelCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ModelCache.java b/src/main/java/org/apache/freemarker/core/model/impl/ModelCache.java
new file mode 100644
index 0000000..529b988
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ModelCache.java
@@ -0,0 +1,143 @@
+/*
+ * 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.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+
+/**
+ * Internally used by various wrapper implementations to implement model
+ * caching.
+ */
+public abstract class ModelCache {
+ private boolean useCache = false;
+ private Map<Object, ModelReference> modelCache = null;
+ private ReferenceQueue<TemplateModel> refQueue = null;
+
+ protected ModelCache() {
+ }
+
+ /**
+ * Sets whether this wrapper caches model instances. Default is false.
+ * When set to true, calling {@link #getInstance(Object)}
+ * multiple times for the same object will return the same model.
+ */
+ public synchronized void setUseCache(boolean useCache) {
+ this.useCache = useCache;
+ if (useCache) {
+ modelCache = new IdentityHashMap<>();
+ refQueue = new ReferenceQueue<>();
+ } else {
+ modelCache = null;
+ refQueue = null;
+ }
+ }
+
+ /**
+ * @since 2.3.21
+ */
+ public synchronized boolean getUseCache() {
+ return useCache;
+ }
+
+ public TemplateModel getInstance(Object object) {
+ if (object instanceof TemplateModel) {
+ return (TemplateModel) object;
+ }
+ if (object instanceof TemplateModelAdapter) {
+ return ((TemplateModelAdapter) object).getTemplateModel();
+ }
+ if (useCache && isCacheable(object)) {
+ TemplateModel model = lookup(object);
+ if (model == null) {
+ model = create(object);
+ register(model, object);
+ }
+ return model;
+ } else {
+ return create(object);
+ }
+ }
+
+ protected abstract TemplateModel create(Object object);
+ protected abstract boolean isCacheable(Object object);
+
+ public void clearCache() {
+ if (modelCache != null) {
+ synchronized (modelCache) {
+ modelCache.clear();
+ }
+ }
+ }
+
+ private TemplateModel lookup(Object object) {
+ ModelReference ref = null;
+ // NOTE: we're doing minimal synchronizations -- which can lead to
+ // duplicate wrapper creation. However, this has no harmful side-effects and
+ // is a lesser performance hit.
+ synchronized (modelCache) {
+ ref = modelCache.get(object);
+ }
+
+ if (ref != null)
+ return ref.getModel();
+
+ return null;
+ }
+
+ private void register(TemplateModel model, Object object) {
+ synchronized (modelCache) {
+ // Remove cleared references
+ for (; ; ) {
+ ModelReference queuedRef = (ModelReference) refQueue.poll();
+ if (queuedRef == null) {
+ break;
+ }
+ modelCache.remove(queuedRef.object);
+ }
+ // Register new reference
+ modelCache.put(object, new ModelReference(model, object, refQueue));
+ }
+ }
+
+ /**
+ * A special soft reference that is registered in the modelCache.
+ * When it gets cleared (that is, the model became unreachable)
+ * it will remove itself from the model cache.
+ */
+ private static final class ModelReference extends SoftReference<TemplateModel> {
+ Object object;
+
+ ModelReference(TemplateModel ref, Object object, ReferenceQueue<TemplateModel> refQueue) {
+ super(ref, refQueue);
+ this.object = object;
+ }
+
+ TemplateModel getModel() {
+ return get();
+ }
+ }
+
+}
\ 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/ModelFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/ModelFactory.java b/src/main/java/org/apache/freemarker/core/model/impl/ModelFactory.java
new file mode 100644
index 0000000..cd74342
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ModelFactory.java
@@ -0,0 +1,34 @@
+/*
+ * 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.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+
+/**
+ * Interface used to create various wrapper models in the {@link ModelCache}.
+ */
+public interface ModelFactory {
+ /**
+ * Create a wrapping model for the specified object that belongs to
+ * the specified wrapper.
+ */
+ TemplateModel create(Object object, ObjectWrapper wrapper);
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/NonPrimitiveArrayBackedReadOnlyList.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/NonPrimitiveArrayBackedReadOnlyList.java b/src/main/java/org/apache/freemarker/core/model/impl/NonPrimitiveArrayBackedReadOnlyList.java
new file mode 100644
index 0000000..2babe9f
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/NonPrimitiveArrayBackedReadOnlyList.java
@@ -0,0 +1,42 @@
+/*
+ * 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.util.AbstractList;
+
+class NonPrimitiveArrayBackedReadOnlyList extends AbstractList {
+
+ private final Object[] array;
+
+ NonPrimitiveArrayBackedReadOnlyList(Object[] array) {
+ this.array = array;
+ }
+
+ @Override
+ public Object get(int index) {
+ return array[index];
+ }
+
+ @Override
+ public int size() {
+ return array.length;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/NumberModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/NumberModel.java b/src/main/java/org/apache/freemarker/core/model/impl/NumberModel.java
new file mode 100644
index 0000000..70b50d9
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/NumberModel.java
@@ -0,0 +1,60 @@
+/*
+ * 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.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateNumberModel;
+
+/**
+ * Wraps arbitrary subclass of {@link java.lang.Number} into a reflective model.
+ * Beside acting as a {@link TemplateNumberModel}, you can call all Java methods on
+ * these objects as well.
+ */
+public class NumberModel
+extends
+ BeanModel
+implements
+ TemplateNumberModel {
+ static final ModelFactory FACTORY =
+ new ModelFactory()
+ {
+ @Override
+ public TemplateModel create(Object object, ObjectWrapper wrapper) {
+ return new NumberModel((Number) object, (DefaultObjectWrapper) wrapper);
+ }
+ };
+ /**
+ * Creates a new model that wraps the specified number object.
+ * @param number the number object to wrap into a model.
+ * @param wrapper the {@link DefaultObjectWrapper} associated with this model.
+ * Every model has to have an associated {@link DefaultObjectWrapper} instance. The
+ * model gains many attributes from its wrapper, including the caching
+ * behavior, method exposure level, method-over-item shadowing policy etc.
+ */
+ public NumberModel(Number number, DefaultObjectWrapper wrapper) {
+ super(number, wrapper);
+ }
+
+ @Override
+ public Number getAsNumber() {
+ return (Number) object;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
new file mode 100644
index 0000000..5b160b6
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedFixArgsMethods.java
@@ -0,0 +1,99 @@
+/*
+ * 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.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * Stores the non-varargs methods for a {@link OverloadedMethods} object.
+ */
+class OverloadedFixArgsMethods extends OverloadedMethodsSubset {
+
+ OverloadedFixArgsMethods() {
+ super();
+ }
+
+ @Override
+ Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) {
+ return memberDesc.getParamTypes();
+ }
+
+ @Override
+ void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes) {
+ // Do nothing
+ }
+
+ @Override
+ MaybeEmptyMemberAndArguments getMemberAndArguments(List tmArgs, DefaultObjectWrapper unwrapper)
+ throws TemplateModelException {
+ if (tmArgs == null) {
+ // null is treated as empty args
+ tmArgs = Collections.EMPTY_LIST;
+ }
+ final int argCount = tmArgs.size();
+ final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
+ if (unwrappingHintsByParamCount.length <= argCount) {
+ return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS;
+ }
+ Class[] unwarppingHints = unwrappingHintsByParamCount[argCount];
+ if (unwarppingHints == null) {
+ return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS;
+ }
+
+ Object[] pojoArgs = new Object[argCount];
+
+ int[] typeFlags = getTypeFlags(argCount);
+ if (typeFlags == ALL_ZEROS_ARRAY) {
+ typeFlags = null;
+ }
+
+ Iterator it = tmArgs.iterator();
+ for (int i = 0; i < argCount; ++i) {
+ Object pojo = unwrapper.tryUnwrapTo(
+ (TemplateModel) it.next(),
+ unwarppingHints[i],
+ typeFlags != null ? typeFlags[i] : 0);
+ if (pojo == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ return EmptyMemberAndArguments.noCompatibleOverload(i + 1);
+ }
+ pojoArgs[i] = pojo;
+ }
+
+ MaybeEmptyCallableMemberDescriptor maybeEmtpyMemberDesc = getMemberDescriptorForArgs(pojoArgs, false);
+ if (maybeEmtpyMemberDesc instanceof CallableMemberDescriptor) {
+ CallableMemberDescriptor memberDesc = (CallableMemberDescriptor) maybeEmtpyMemberDesc;
+ if (typeFlags != null) {
+ // Note that overloaded method selection has already accounted for overflow errors when the method
+ // was selected. So this forced conversion shouldn't cause such corruption. Except, conversion from
+ // BigDecimal is allowed to overflow for backward-compatibility.
+ forceNumberArgumentsToParameterTypes(pojoArgs, memberDesc.getParamTypes(), typeFlags);
+ }
+ return new MemberAndArguments(memberDesc, pojoArgs);
+ } else {
+ return EmptyMemberAndArguments.from((EmptyCallableMemberDescriptor) maybeEmtpyMemberDesc, pojoArgs);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
new file mode 100644
index 0000000..e352bcf
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/model/impl/OverloadedMethods.java
@@ -0,0 +1,271 @@
+/*
+ * 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.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.freemarker.core._DelayedConversionToString;
+import org.apache.freemarker.core._ErrorDescriptionBuilder;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util._ClassUtil;
+
+/**
+ * Used instead of {@link java.lang.reflect.Method} or {@link java.lang.reflect.Constructor} for overloaded methods and
+ * constructors.
+ *
+ * <p>After the initialization with the {@link #addMethod(Method)} and {@link #addConstructor(Constructor)} calls are
+ * done, the instance must be thread-safe. Before that, it's the responsibility of the caller of those methods to
+ * ensure that the object is properly publishing to other threads.
+ */
+final class OverloadedMethods {
+
+ private final OverloadedMethodsSubset fixArgMethods;
+ private OverloadedMethodsSubset varargMethods;
+
+ OverloadedMethods() {
+ fixArgMethods = new OverloadedFixArgsMethods();
+ }
+
+ void addMethod(Method method) {
+ final Class[] paramTypes = method.getParameterTypes();
+ addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(method, paramTypes));
+ }
+
+ void addConstructor(Constructor constr) {
+ final Class[] paramTypes = constr.getParameterTypes();
+ addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(constr, paramTypes));
+ }
+
+ private void addCallableMemberDescriptor(ReflectionCallableMemberDescriptor memberDesc) {
+ // Note: "varargs" methods are always callable as oms args, with a sequence (array) as the last parameter.
+ fixArgMethods.addCallableMemberDescriptor(memberDesc);
+ if (memberDesc.isVarargs()) {
+ if (varargMethods == null) {
+ varargMethods = new OverloadedVarArgsMethods();
+ }
+ varargMethods.addCallableMemberDescriptor(memberDesc);
+ }
+ }
+
+ MemberAndArguments getMemberAndArguments(List/*<TemplateModel>*/ tmArgs, DefaultObjectWrapper unwrapper)
+ throws TemplateModelException {
+ // Try to find a oms args match:
+ MaybeEmptyMemberAndArguments fixArgsRes = fixArgMethods.getMemberAndArguments(tmArgs, unwrapper);
+ if (fixArgsRes instanceof MemberAndArguments) {
+ return (MemberAndArguments) fixArgsRes;
+ }
+
+ // Try to find a varargs match:
+ MaybeEmptyMemberAndArguments varargsRes;
+ if (varargMethods != null) {
+ varargsRes = varargMethods.getMemberAndArguments(tmArgs, unwrapper);
+ if (varargsRes instanceof MemberAndArguments) {
+ return (MemberAndArguments) varargsRes;
+ }
+ } else {
+ varargsRes = null;
+ }
+
+ _ErrorDescriptionBuilder edb = new _ErrorDescriptionBuilder(
+ toCompositeErrorMessage(
+ (EmptyMemberAndArguments) fixArgsRes,
+ (EmptyMemberAndArguments) varargsRes,
+ tmArgs),
+ "\nThe matching overload was searched among these members:\n",
+ memberListToString());
+ addMarkupBITipAfterNoNoMarchIfApplicable(edb, tmArgs);
+ throw new _TemplateModelException(edb);
+ }
+
+ private Object[] toCompositeErrorMessage(
+ final EmptyMemberAndArguments fixArgsEmptyRes, final EmptyMemberAndArguments varargsEmptyRes,
+ List tmArgs) {
+ final Object[] argsErrorMsg;
+ if (varargsEmptyRes != null) {
+ if (fixArgsEmptyRes == null || fixArgsEmptyRes.isNumberOfArgumentsWrong()) {
+ argsErrorMsg = toErrorMessage(varargsEmptyRes, tmArgs);
+ } else {
+ argsErrorMsg = new Object[] {
+ "When trying to call the non-varargs overloads:\n",
+ toErrorMessage(fixArgsEmptyRes, tmArgs),
+ "\nWhen trying to call the varargs overloads:\n",
+ toErrorMessage(varargsEmptyRes, null)
+ };
+ }
+ } else {
+ argsErrorMsg = toErrorMessage(fixArgsEmptyRes, tmArgs);
+ }
+ return argsErrorMsg;
+ }
+
+ private Object[] toErrorMessage(EmptyMemberAndArguments res, List/*<TemplateModel>*/ tmArgs) {
+ final Object[] unwrappedArgs = res.getUnwrappedArguments();
+ return new Object[] {
+ res.getErrorDescription(),
+ tmArgs != null
+ ? new Object[] {
+ "\nThe FTL type of the argument values were: ", getTMActualParameterTypes(tmArgs), "." }
+ : "",
+ unwrappedArgs != null
+ ? new Object[] {
+ "\nThe Java type of the argument values were: ",
+ getUnwrappedActualParameterTypes(unwrappedArgs) + "." }
+ : ""};
+ }
+
+ private _DelayedConversionToString memberListToString() {
+ return new _DelayedConversionToString(null) {
+
+ @Override
+ protected String doConversion(Object obj) {
+ final Iterator fixArgMethodsIter = fixArgMethods.getMemberDescriptors();
+ final Iterator varargMethodsIter = varargMethods != null ? varargMethods.getMemberDescriptors() : null;
+
+ boolean hasMethods = fixArgMethodsIter.hasNext() || (varargMethodsIter != null && varargMethodsIter.hasNext());
+ if (hasMethods) {
+ StringBuilder sb = new StringBuilder();
+ HashSet fixArgMethods = new HashSet();
+ while (fixArgMethodsIter.hasNext()) {
+ if (sb.length() != 0) sb.append(",\n");
+ sb.append(" ");
+ CallableMemberDescriptor callableMemberDesc = (CallableMemberDescriptor) fixArgMethodsIter.next();
+ fixArgMethods.add(callableMemberDesc);
+ sb.append(callableMemberDesc.getDeclaration());
+ }
+ if (varargMethodsIter != null) {
+ while (varargMethodsIter.hasNext()) {
+ CallableMemberDescriptor callableMemberDesc = (CallableMemberDescriptor) varargMethodsIter.next();
+ if (!fixArgMethods.contains(callableMemberDesc)) {
+ if (sb.length() != 0) sb.append(",\n");
+ sb.append(" ");
+ sb.append(callableMemberDesc.getDeclaration());
+ }
+ }
+ }
+ return sb.toString();
+ } else {
+ return "No members";
+ }
+ }
+
+ };
+ }
+
+ /**
+ * Adds tip to the error message if converting a {@link TemplateMarkupOutputModel} argument to {@link String} might
+ * allows finding a matching overload.
+ */
+ private void addMarkupBITipAfterNoNoMarchIfApplicable(_ErrorDescriptionBuilder edb,
+ List tmArgs) {
+ for (int argIdx = 0; argIdx < tmArgs.size(); argIdx++) {
+ Object tmArg = tmArgs.get(argIdx);
+ if (tmArg instanceof TemplateMarkupOutputModel) {
+ for (Iterator membDescs = fixArgMethods.getMemberDescriptors(); membDescs.hasNext();) {
+ CallableMemberDescriptor membDesc = (CallableMemberDescriptor) membDescs.next();
+ Class[] paramTypes = membDesc.getParamTypes();
+
+ Class paramType = null;
+ if (membDesc.isVarargs() && argIdx >= paramTypes.length - 1) {
+ paramType = paramTypes[paramTypes.length - 1];
+ if (paramType.isArray()) {
+ paramType = paramType.getComponentType();
+ }
+ }
+ if (paramType == null && argIdx < paramTypes.length) {
+ paramType = paramTypes[argIdx];
+ }
+ if (paramType != null) {
+ if (paramType.isAssignableFrom(String.class) && !paramType.isAssignableFrom(tmArg.getClass())) {
+ edb.tip(SimpleMethodModel.MARKUP_OUTPUT_TO_STRING_TIP);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private _DelayedConversionToString getTMActualParameterTypes(List arguments) {
+ final String[] argumentTypeDescs = new String[arguments.size()];
+ for (int i = 0; i < arguments.size(); i++) {
+ argumentTypeDescs[i] = FTLUtil.getTypeDescription((TemplateModel) arguments.get(i));
+ }
+
+ return new DelayedCallSignatureToString(argumentTypeDescs) {
+
+ @Override
+ String argumentToString(Object argType) {
+ return (String) argType;
+ }
+
+ };
+ }
+
+ private Object getUnwrappedActualParameterTypes(Object[] unwrappedArgs) {
+ final Class[] argumentTypes = new Class[unwrappedArgs.length];
+ for (int i = 0; i < unwrappedArgs.length; i++) {
+ Object unwrappedArg = unwrappedArgs[i];
+ argumentTypes[i] = unwrappedArg != null ? unwrappedArg.getClass() : null;
+ }
+
+ return new DelayedCallSignatureToString(argumentTypes) {
+
+ @Override
+ String argumentToString(Object argType) {
+ return argType != null
+ ? _ClassUtil.getShortClassName((Class) argType)
+ : _ClassUtil.getShortClassNameOfObject(null);
+ }
+
+ };
+ }
+
+ private abstract class DelayedCallSignatureToString extends _DelayedConversionToString {
+
+ public DelayedCallSignatureToString(Object[] argTypeArray) {
+ super(argTypeArray);
+ }
+
+ @Override
+ protected String doConversion(Object obj) {
+ Object[] argTypes = (Object[]) obj;
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < argTypes.length; i++) {
+ if (i != 0) sb.append(", ");
+ sb.append(argumentToString(argTypes[i]));
+ }
+
+ return sb.toString();
+ }
+
+ abstract String argumentToString(Object argType);
+
+ }
+
+}
[03/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/Java7MembersOnlyBeansWrapper.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/Java7MembersOnlyBeansWrapper.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/Java7MembersOnlyBeansWrapper.java
deleted file mode 100644
index b4cff32..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/Java7MembersOnlyBeansWrapper.java
+++ /dev/null
@@ -1,102 +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.Method;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.Version;
-
-/**
- * A hack to keep some unit tests producing exactly the same results on Java 8 as on Java 5-7, by hiding members
- * added after Java 7.
- */
-public class Java7MembersOnlyBeansWrapper extends BeansWrapper {
-
- private static final Set<String> POST_JAVA_7_MAP_METHODS = newHashSet(
- "compute", "computeIfAbsent", "computeIfPresent",
- "forEach", "getOrDefault", "merge", "putIfAbsent", "replace", "replaceAll");
-
- private static final Set<String> POST_JAVA_7_ITERABLE_METHODS = newHashSet("forEach");
- private static final Set<String> POST_JAVA_7_COLLECTION_METHODS = newHashSet("parallelStream", "removeIf", "stream");
- private static final Set<String> POST_JAVA_7_LIST_METHODS = newHashSet("sort", "spliterator");
-
- MethodAppearanceFineTuner POST_JAVA_7_FILTER = new MethodAppearanceFineTuner() {
-
- @Override
- public void process(MethodAppearanceDecisionInput in, MethodAppearanceDecision out) {
- Method m = in.getMethod();
- Class declCl = m.getDeclaringClass();
- if (Map.class.isAssignableFrom(declCl)) {
- if (POST_JAVA_7_MAP_METHODS.contains(m.getName())) {
- hideMember(out);
- return;
- }
- }
- if (Iterable.class.isAssignableFrom(declCl)) {
- if (POST_JAVA_7_ITERABLE_METHODS.contains(m.getName())) {
- hideMember(out);
- return;
- }
- }
- if (Collection.class.isAssignableFrom(declCl)) {
- if (POST_JAVA_7_COLLECTION_METHODS.contains(m.getName())) {
- hideMember(out);
- return;
- }
- }
- if (List.class.isAssignableFrom(declCl)) {
- if (POST_JAVA_7_LIST_METHODS.contains(m.getName())) {
- hideMember(out);
- return;
- }
- }
- }
-
- private void hideMember(MethodAppearanceDecision out) {
- out.setExposeMethodAs(null);
- out.setExposeAsProperty(null);
- }
-
- };
-
- public Java7MembersOnlyBeansWrapper(Version version) {
- super(version);
- setMethodAppearanceFineTuner(POST_JAVA_7_FILTER);
- }
-
- private static <T> Set<T> newHashSet(T... items) {
- HashSet<T> r = new HashSet<>();
- for (T item : items) {
- r.add(item);
- }
- return r;
- }
-
- public Java7MembersOnlyBeansWrapper() {
- this(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/ManyObjectsOfDifferentClasses.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/ManyObjectsOfDifferentClasses.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/ManyObjectsOfDifferentClasses.java
deleted file mode 100644
index 149bf22..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/ManyObjectsOfDifferentClasses.java
+++ /dev/null
@@ -1,249 +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;
-
-// FMPP template source:
-// ----
-// package org.apache.freemarker.core.model.impl.beans;
-//
-// // FMPP template source:
-// // ----
-// <#assign src><#include "ManyObjectsOfDifferentClasses.java.ftl" parse=false></#assign>
-// ${src?trim?replace('(.*\r?\n)|.+', r'// $0', 'r')}
-// // ----
-//
-// <#assign MAX_CLA = 8 - 1>
-// <#assign MAX_MET = 10 - 1>
-// <#assign MAX_PRO = 10 - 1>
-//
-// public class ManyObjectsOfDifferentClasses {
-//
-// public static final Object[] OBJECTS = new Object[] {
-// <#list 0..MAX_CLA as claI>
-// new C${claI}(),
-// </#list>
-// };
-//
-// <#list 0..MAX_CLA as claI>
-// static public class C${claI} {
-// <#list 0..MAX_PRO as proI>
-// public int getP${proI}() { return ${claI} * 1000 + ${proI}; }
-// </#list>
-// <#list 0..MAX_MET as metI>
-// public int m${metI}() { return ${claI} * 1000 + ${metI}; };
-// </#list>
-// }
-// </#list>
-//
-// }
-// ----
-
-
-public class ManyObjectsOfDifferentClasses {
-
- public static final Object[] OBJECTS = new Object[] {
- new C0(),
- new C1(),
- new C2(),
- new C3(),
- new C4(),
- new C5(),
- new C6(),
- new C7(),
- };
-
- static public class C0 {
- public int getP0() { return 0 * 1000 + 0; }
- public int getP1() { return 0 * 1000 + 1; }
- public int getP2() { return 0 * 1000 + 2; }
- public int getP3() { return 0 * 1000 + 3; }
- public int getP4() { return 0 * 1000 + 4; }
- public int getP5() { return 0 * 1000 + 5; }
- public int getP6() { return 0 * 1000 + 6; }
- public int getP7() { return 0 * 1000 + 7; }
- public int getP8() { return 0 * 1000 + 8; }
- public int getP9() { return 0 * 1000 + 9; }
- public int m0() { return 0 * 1000 + 0; };
- public int m1() { return 0 * 1000 + 1; };
- public int m2() { return 0 * 1000 + 2; };
- public int m3() { return 0 * 1000 + 3; };
- public int m4() { return 0 * 1000 + 4; };
- public int m5() { return 0 * 1000 + 5; };
- public int m6() { return 0 * 1000 + 6; };
- public int m7() { return 0 * 1000 + 7; };
- public int m8() { return 0 * 1000 + 8; };
- public int m9() { return 0 * 1000 + 9; };
- }
- static public class C1 {
- public int getP0() { return 1 * 1000 + 0; }
- public int getP1() { return 1 * 1000 + 1; }
- public int getP2() { return 1 * 1000 + 2; }
- public int getP3() { return 1 * 1000 + 3; }
- public int getP4() { return 1 * 1000 + 4; }
- public int getP5() { return 1 * 1000 + 5; }
- public int getP6() { return 1 * 1000 + 6; }
- public int getP7() { return 1 * 1000 + 7; }
- public int getP8() { return 1 * 1000 + 8; }
- public int getP9() { return 1 * 1000 + 9; }
- public int m0() { return 1 * 1000 + 0; };
- public int m1() { return 1 * 1000 + 1; };
- public int m2() { return 1 * 1000 + 2; };
- public int m3() { return 1 * 1000 + 3; };
- public int m4() { return 1 * 1000 + 4; };
- public int m5() { return 1 * 1000 + 5; };
- public int m6() { return 1 * 1000 + 6; };
- public int m7() { return 1 * 1000 + 7; };
- public int m8() { return 1 * 1000 + 8; };
- public int m9() { return 1 * 1000 + 9; };
- }
- static public class C2 {
- public int getP0() { return 2 * 1000 + 0; }
- public int getP1() { return 2 * 1000 + 1; }
- public int getP2() { return 2 * 1000 + 2; }
- public int getP3() { return 2 * 1000 + 3; }
- public int getP4() { return 2 * 1000 + 4; }
- public int getP5() { return 2 * 1000 + 5; }
- public int getP6() { return 2 * 1000 + 6; }
- public int getP7() { return 2 * 1000 + 7; }
- public int getP8() { return 2 * 1000 + 8; }
- public int getP9() { return 2 * 1000 + 9; }
- public int m0() { return 2 * 1000 + 0; };
- public int m1() { return 2 * 1000 + 1; };
- public int m2() { return 2 * 1000 + 2; };
- public int m3() { return 2 * 1000 + 3; };
- public int m4() { return 2 * 1000 + 4; };
- public int m5() { return 2 * 1000 + 5; };
- public int m6() { return 2 * 1000 + 6; };
- public int m7() { return 2 * 1000 + 7; };
- public int m8() { return 2 * 1000 + 8; };
- public int m9() { return 2 * 1000 + 9; };
- }
- static public class C3 {
- public int getP0() { return 3 * 1000 + 0; }
- public int getP1() { return 3 * 1000 + 1; }
- public int getP2() { return 3 * 1000 + 2; }
- public int getP3() { return 3 * 1000 + 3; }
- public int getP4() { return 3 * 1000 + 4; }
- public int getP5() { return 3 * 1000 + 5; }
- public int getP6() { return 3 * 1000 + 6; }
- public int getP7() { return 3 * 1000 + 7; }
- public int getP8() { return 3 * 1000 + 8; }
- public int getP9() { return 3 * 1000 + 9; }
- public int m0() { return 3 * 1000 + 0; };
- public int m1() { return 3 * 1000 + 1; };
- public int m2() { return 3 * 1000 + 2; };
- public int m3() { return 3 * 1000 + 3; };
- public int m4() { return 3 * 1000 + 4; };
- public int m5() { return 3 * 1000 + 5; };
- public int m6() { return 3 * 1000 + 6; };
- public int m7() { return 3 * 1000 + 7; };
- public int m8() { return 3 * 1000 + 8; };
- public int m9() { return 3 * 1000 + 9; };
- }
- static public class C4 {
- public int getP0() { return 4 * 1000 + 0; }
- public int getP1() { return 4 * 1000 + 1; }
- public int getP2() { return 4 * 1000 + 2; }
- public int getP3() { return 4 * 1000 + 3; }
- public int getP4() { return 4 * 1000 + 4; }
- public int getP5() { return 4 * 1000 + 5; }
- public int getP6() { return 4 * 1000 + 6; }
- public int getP7() { return 4 * 1000 + 7; }
- public int getP8() { return 4 * 1000 + 8; }
- public int getP9() { return 4 * 1000 + 9; }
- public int m0() { return 4 * 1000 + 0; };
- public int m1() { return 4 * 1000 + 1; };
- public int m2() { return 4 * 1000 + 2; };
- public int m3() { return 4 * 1000 + 3; };
- public int m4() { return 4 * 1000 + 4; };
- public int m5() { return 4 * 1000 + 5; };
- public int m6() { return 4 * 1000 + 6; };
- public int m7() { return 4 * 1000 + 7; };
- public int m8() { return 4 * 1000 + 8; };
- public int m9() { return 4 * 1000 + 9; };
- }
- static public class C5 {
- public int getP0() { return 5 * 1000 + 0; }
- public int getP1() { return 5 * 1000 + 1; }
- public int getP2() { return 5 * 1000 + 2; }
- public int getP3() { return 5 * 1000 + 3; }
- public int getP4() { return 5 * 1000 + 4; }
- public int getP5() { return 5 * 1000 + 5; }
- public int getP6() { return 5 * 1000 + 6; }
- public int getP7() { return 5 * 1000 + 7; }
- public int getP8() { return 5 * 1000 + 8; }
- public int getP9() { return 5 * 1000 + 9; }
- public int m0() { return 5 * 1000 + 0; };
- public int m1() { return 5 * 1000 + 1; };
- public int m2() { return 5 * 1000 + 2; };
- public int m3() { return 5 * 1000 + 3; };
- public int m4() { return 5 * 1000 + 4; };
- public int m5() { return 5 * 1000 + 5; };
- public int m6() { return 5 * 1000 + 6; };
- public int m7() { return 5 * 1000 + 7; };
- public int m8() { return 5 * 1000 + 8; };
- public int m9() { return 5 * 1000 + 9; };
- }
- static public class C6 {
- public int getP0() { return 6 * 1000 + 0; }
- public int getP1() { return 6 * 1000 + 1; }
- public int getP2() { return 6 * 1000 + 2; }
- public int getP3() { return 6 * 1000 + 3; }
- public int getP4() { return 6 * 1000 + 4; }
- public int getP5() { return 6 * 1000 + 5; }
- public int getP6() { return 6 * 1000 + 6; }
- public int getP7() { return 6 * 1000 + 7; }
- public int getP8() { return 6 * 1000 + 8; }
- public int getP9() { return 6 * 1000 + 9; }
- public int m0() { return 6 * 1000 + 0; };
- public int m1() { return 6 * 1000 + 1; };
- public int m2() { return 6 * 1000 + 2; };
- public int m3() { return 6 * 1000 + 3; };
- public int m4() { return 6 * 1000 + 4; };
- public int m5() { return 6 * 1000 + 5; };
- public int m6() { return 6 * 1000 + 6; };
- public int m7() { return 6 * 1000 + 7; };
- public int m8() { return 6 * 1000 + 8; };
- public int m9() { return 6 * 1000 + 9; };
- }
- static public class C7 {
- public int getP0() { return 7 * 1000 + 0; }
- public int getP1() { return 7 * 1000 + 1; }
- public int getP2() { return 7 * 1000 + 2; }
- public int getP3() { return 7 * 1000 + 3; }
- public int getP4() { return 7 * 1000 + 4; }
- public int getP5() { return 7 * 1000 + 5; }
- public int getP6() { return 7 * 1000 + 6; }
- public int getP7() { return 7 * 1000 + 7; }
- public int getP8() { return 7 * 1000 + 8; }
- public int getP9() { return 7 * 1000 + 9; }
- public int m0() { return 7 * 1000 + 0; };
- public int m1() { return 7 * 1000 + 1; };
- public int m2() { return 7 * 1000 + 2; };
- public int m3() { return 7 * 1000 + 3; };
- public int m4() { return 7 * 1000 + 4; };
- public int m5() { return 7 * 1000 + 5; };
- public int m6() { return 7 * 1000 + 6; };
- public int m7() { return 7 * 1000 + 7; };
- public int m8() { return 7 * 1000 + 8; };
- public int m9() { return 7 * 1000 + 9; };
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/ManyStaticsOfDifferentClasses.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/ManyStaticsOfDifferentClasses.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/ManyStaticsOfDifferentClasses.java
deleted file mode 100644
index bffe56d..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/ManyStaticsOfDifferentClasses.java
+++ /dev/null
@@ -1,236 +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;
-
-// FMPP template source:
-// ----
-// package org.apache.freemarker.core.model.impl.beans;
-//
-// // FMPP template source:
-// // ----
-// <#assign src><#include "ManyStaticsOfDifferentClasses.java.ftl" parse=false></#assign>
-// ${src?trim?replace('(.*\r?\n)|.+', r'// $0', 'r')}
-// // ----
-//
-// <#assign MAX_CLA = 8 - 1>
-// <#assign MAX_MET = 10 - 1>
-// <#assign MAX_PRO = 10 - 1>
-//
-// public class ManyStaticsOfDifferentClasses {
-//
-// private ManyStaticsOfDifferentClasses() { }
-//
-// <#list 0..MAX_CLA as claI>
-// static public class C${claI} {
-// <#list 0..MAX_PRO as proI>
-// public static int p${proI} = ${claI} * 1000 + ${proI};
-// </#list>
-// <#list 0..MAX_MET as metI>
-// public static int m${metI}() { return ${claI} * 1000 + ${metI}; };
-// </#list>
-// }
-// </#list>
-//
-// }
-// ----
-
-
-public class ManyStaticsOfDifferentClasses {
-
- private ManyStaticsOfDifferentClasses() { }
-
- static public class C0 {
- public static int p0 = 0 * 1000 + 0;
- public static int p1 = 0 * 1000 + 1;
- public static int p2 = 0 * 1000 + 2;
- public static int p3 = 0 * 1000 + 3;
- public static int p4 = 0 * 1000 + 4;
- public static int p5 = 0 * 1000 + 5;
- public static int p6 = 0 * 1000 + 6;
- public static int p7 = 0 * 1000 + 7;
- public static int p8 = 0 * 1000 + 8;
- public static int p9 = 0 * 1000 + 9;
- public static int m0() { return 0 * 1000 + 0; };
- public static int m1() { return 0 * 1000 + 1; };
- public static int m2() { return 0 * 1000 + 2; };
- public static int m3() { return 0 * 1000 + 3; };
- public static int m4() { return 0 * 1000 + 4; };
- public static int m5() { return 0 * 1000 + 5; };
- public static int m6() { return 0 * 1000 + 6; };
- public static int m7() { return 0 * 1000 + 7; };
- public static int m8() { return 0 * 1000 + 8; };
- public static int m9() { return 0 * 1000 + 9; };
- }
- static public class C1 {
- public static int p0 = 1 * 1000 + 0;
- public static int p1 = 1 * 1000 + 1;
- public static int p2 = 1 * 1000 + 2;
- public static int p3 = 1 * 1000 + 3;
- public static int p4 = 1 * 1000 + 4;
- public static int p5 = 1 * 1000 + 5;
- public static int p6 = 1 * 1000 + 6;
- public static int p7 = 1 * 1000 + 7;
- public static int p8 = 1 * 1000 + 8;
- public static int p9 = 1 * 1000 + 9;
- public static int m0() { return 1 * 1000 + 0; };
- public static int m1() { return 1 * 1000 + 1; };
- public static int m2() { return 1 * 1000 + 2; };
- public static int m3() { return 1 * 1000 + 3; };
- public static int m4() { return 1 * 1000 + 4; };
- public static int m5() { return 1 * 1000 + 5; };
- public static int m6() { return 1 * 1000 + 6; };
- public static int m7() { return 1 * 1000 + 7; };
- public static int m8() { return 1 * 1000 + 8; };
- public static int m9() { return 1 * 1000 + 9; };
- }
- static public class C2 {
- public static int p0 = 2 * 1000 + 0;
- public static int p1 = 2 * 1000 + 1;
- public static int p2 = 2 * 1000 + 2;
- public static int p3 = 2 * 1000 + 3;
- public static int p4 = 2 * 1000 + 4;
- public static int p5 = 2 * 1000 + 5;
- public static int p6 = 2 * 1000 + 6;
- public static int p7 = 2 * 1000 + 7;
- public static int p8 = 2 * 1000 + 8;
- public static int p9 = 2 * 1000 + 9;
- public static int m0() { return 2 * 1000 + 0; };
- public static int m1() { return 2 * 1000 + 1; };
- public static int m2() { return 2 * 1000 + 2; };
- public static int m3() { return 2 * 1000 + 3; };
- public static int m4() { return 2 * 1000 + 4; };
- public static int m5() { return 2 * 1000 + 5; };
- public static int m6() { return 2 * 1000 + 6; };
- public static int m7() { return 2 * 1000 + 7; };
- public static int m8() { return 2 * 1000 + 8; };
- public static int m9() { return 2 * 1000 + 9; };
- }
- static public class C3 {
- public static int p0 = 3 * 1000 + 0;
- public static int p1 = 3 * 1000 + 1;
- public static int p2 = 3 * 1000 + 2;
- public static int p3 = 3 * 1000 + 3;
- public static int p4 = 3 * 1000 + 4;
- public static int p5 = 3 * 1000 + 5;
- public static int p6 = 3 * 1000 + 6;
- public static int p7 = 3 * 1000 + 7;
- public static int p8 = 3 * 1000 + 8;
- public static int p9 = 3 * 1000 + 9;
- public static int m0() { return 3 * 1000 + 0; };
- public static int m1() { return 3 * 1000 + 1; };
- public static int m2() { return 3 * 1000 + 2; };
- public static int m3() { return 3 * 1000 + 3; };
- public static int m4() { return 3 * 1000 + 4; };
- public static int m5() { return 3 * 1000 + 5; };
- public static int m6() { return 3 * 1000 + 6; };
- public static int m7() { return 3 * 1000 + 7; };
- public static int m8() { return 3 * 1000 + 8; };
- public static int m9() { return 3 * 1000 + 9; };
- }
- static public class C4 {
- public static int p0 = 4 * 1000 + 0;
- public static int p1 = 4 * 1000 + 1;
- public static int p2 = 4 * 1000 + 2;
- public static int p3 = 4 * 1000 + 3;
- public static int p4 = 4 * 1000 + 4;
- public static int p5 = 4 * 1000 + 5;
- public static int p6 = 4 * 1000 + 6;
- public static int p7 = 4 * 1000 + 7;
- public static int p8 = 4 * 1000 + 8;
- public static int p9 = 4 * 1000 + 9;
- public static int m0() { return 4 * 1000 + 0; };
- public static int m1() { return 4 * 1000 + 1; };
- public static int m2() { return 4 * 1000 + 2; };
- public static int m3() { return 4 * 1000 + 3; };
- public static int m4() { return 4 * 1000 + 4; };
- public static int m5() { return 4 * 1000 + 5; };
- public static int m6() { return 4 * 1000 + 6; };
- public static int m7() { return 4 * 1000 + 7; };
- public static int m8() { return 4 * 1000 + 8; };
- public static int m9() { return 4 * 1000 + 9; };
- }
- static public class C5 {
- public static int p0 = 5 * 1000 + 0;
- public static int p1 = 5 * 1000 + 1;
- public static int p2 = 5 * 1000 + 2;
- public static int p3 = 5 * 1000 + 3;
- public static int p4 = 5 * 1000 + 4;
- public static int p5 = 5 * 1000 + 5;
- public static int p6 = 5 * 1000 + 6;
- public static int p7 = 5 * 1000 + 7;
- public static int p8 = 5 * 1000 + 8;
- public static int p9 = 5 * 1000 + 9;
- public static int m0() { return 5 * 1000 + 0; };
- public static int m1() { return 5 * 1000 + 1; };
- public static int m2() { return 5 * 1000 + 2; };
- public static int m3() { return 5 * 1000 + 3; };
- public static int m4() { return 5 * 1000 + 4; };
- public static int m5() { return 5 * 1000 + 5; };
- public static int m6() { return 5 * 1000 + 6; };
- public static int m7() { return 5 * 1000 + 7; };
- public static int m8() { return 5 * 1000 + 8; };
- public static int m9() { return 5 * 1000 + 9; };
- }
- static public class C6 {
- public static int p0 = 6 * 1000 + 0;
- public static int p1 = 6 * 1000 + 1;
- public static int p2 = 6 * 1000 + 2;
- public static int p3 = 6 * 1000 + 3;
- public static int p4 = 6 * 1000 + 4;
- public static int p5 = 6 * 1000 + 5;
- public static int p6 = 6 * 1000 + 6;
- public static int p7 = 6 * 1000 + 7;
- public static int p8 = 6 * 1000 + 8;
- public static int p9 = 6 * 1000 + 9;
- public static int m0() { return 6 * 1000 + 0; };
- public static int m1() { return 6 * 1000 + 1; };
- public static int m2() { return 6 * 1000 + 2; };
- public static int m3() { return 6 * 1000 + 3; };
- public static int m4() { return 6 * 1000 + 4; };
- public static int m5() { return 6 * 1000 + 5; };
- public static int m6() { return 6 * 1000 + 6; };
- public static int m7() { return 6 * 1000 + 7; };
- public static int m8() { return 6 * 1000 + 8; };
- public static int m9() { return 6 * 1000 + 9; };
- }
- static public class C7 {
- public static int p0 = 7 * 1000 + 0;
- public static int p1 = 7 * 1000 + 1;
- public static int p2 = 7 * 1000 + 2;
- public static int p3 = 7 * 1000 + 3;
- public static int p4 = 7 * 1000 + 4;
- public static int p5 = 7 * 1000 + 5;
- public static int p6 = 7 * 1000 + 6;
- public static int p7 = 7 * 1000 + 7;
- public static int p8 = 7 * 1000 + 8;
- public static int p9 = 7 * 1000 + 9;
- public static int m0() { return 7 * 1000 + 0; };
- public static int m1() { return 7 * 1000 + 1; };
- public static int m2() { return 7 * 1000 + 2; };
- public static int m3() { return 7 * 1000 + 3; };
- public static int m4() { return 7 * 1000 + 4; };
- public static int m5() { return 7 * 1000 + 5; };
- public static int m6() { return 7 * 1000 + 6; };
- public static int m7() { return 7 * 1000 + 7; };
- public static int m8() { return 7 * 1000 + 8; };
- public static int m9() { return 7 * 1000 + 9; };
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/MiscNumericalOperationsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/MiscNumericalOperationsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/MiscNumericalOperationsTest.java
deleted file mode 100644
index d88777f..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/MiscNumericalOperationsTest.java
+++ /dev/null
@@ -1,111 +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.math.BigDecimal;
-import java.math.BigInteger;
-
-import org.junit.Assert;
-
-import junit.framework.TestCase;
-
-public class MiscNumericalOperationsTest extends TestCase {
-
- public MiscNumericalOperationsTest(String name) {
- super(name);
- }
-
- public void testForceUnwrappedNumberToType() {
- // Usual type to to all other types:
- Double n = Double.valueOf(123.75);
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Short.class), Short.valueOf(n.shortValue()));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Short.TYPE), Short.valueOf(n.shortValue()));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Byte.class), Byte.valueOf(n.byteValue()));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Byte.TYPE), Byte.valueOf(n.byteValue()));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Integer.class), Integer.valueOf(n.intValue()));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Integer.TYPE), Integer.valueOf(n.intValue()));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Long.class), Long.valueOf(n.longValue()));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Long.TYPE), Long.valueOf(n.longValue()));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Float.class), Float.valueOf(n.floatValue()));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, Float.TYPE), Float.valueOf(n.floatValue()));
- assertTrue(BeansWrapper.forceUnwrappedNumberToType(n, Double.class) == n);
- assertTrue(BeansWrapper.forceUnwrappedNumberToType(n, Double.TYPE) == n);
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, BigInteger.class), new BigInteger("123"));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(n, BigDecimal.class), new BigDecimal("123.75"));
-
- // Cases of conversion to BigDecimal:
- BigDecimal bd = new BigDecimal("123");
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(new BigInteger("123"), BigDecimal.class), bd);
- assertTrue(BeansWrapper.forceUnwrappedNumberToType(bd, BigDecimal.class) == bd);
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(Long.valueOf(123), BigDecimal.class), bd);
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(Double.valueOf(123), BigDecimal.class), bd);
-
- // Cases of conversion to BigInteger:
- BigInteger bi = new BigInteger("123");
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(new BigDecimal("123.6"), BigInteger.class), bi);
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(
- new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal("123")), BigInteger.class), bi);
- assertTrue(BeansWrapper.forceUnwrappedNumberToType(bi, BigInteger.class) == bi);
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(Long.valueOf(123), BigInteger.class), bi);
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(Double.valueOf(123.6), BigInteger.class), bi);
-
- assertTrue(BeansWrapper.forceUnwrappedNumberToType(n, Number.class) == n);
- assertNull(BeansWrapper.forceUnwrappedNumberToType(n, RationalNumber.class));
- RationalNumber r = new RationalNumber(1, 2);
- assertTrue(BeansWrapper.forceUnwrappedNumberToType(r, RationalNumber.class) == r);
- assertTrue(BeansWrapper.forceUnwrappedNumberToType(r, Number.class) == r);
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(r, Double.class), Double.valueOf(0.5));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(r, BigDecimal.class), new BigDecimal("0.5"));
- assertEquals(BeansWrapper.forceUnwrappedNumberToType(r, BigInteger.class), BigInteger.ZERO);
- }
-
- @SuppressWarnings("boxing")
- public void testForceNumberArgumentsToParameterTypes() {
- OverloadedMethodsSubset oms = new OverloadedFixArgsMethods();
- Class[] paramTypes = new Class[] { Short.TYPE, Short.class, Double.TYPE, BigDecimal.class, BigInteger.class };
- Object[] args;
-
- args = newArgs();
- oms.forceNumberArgumentsToParameterTypes(args, paramTypes, new int[] { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF });
- Assert.assertArrayEquals(
- args,
- new Object[] { (short) 123, (short) 123, 123.75, new BigDecimal("123.75"), BigInteger.valueOf(123) });
-
- args = newArgs();
- oms.forceNumberArgumentsToParameterTypes(args, paramTypes, new int[] { 0, 0, 0, 0, 0 });
- Assert.assertArrayEquals(args, newArgs());
-
- args = newArgs();
- oms.forceNumberArgumentsToParameterTypes(args, paramTypes, new int[] { 8, 8, 8, 8, 8 });
- Assert.assertArrayEquals(args, newArgs());
-
- args = newArgs();
- oms.forceNumberArgumentsToParameterTypes(args, paramTypes, new int[] { 0xFFFF, 0, 0xFFFF, 0, 0xFFFF });
- Assert.assertArrayEquals(
- args,
- new Object[] { (short) 123, 123.75, 123.75, 123.75, BigInteger.valueOf(123) });
- }
-
- @SuppressWarnings("boxing")
- private Object[] newArgs() {
- return new Object[] { 123.75, 123.75, 123.75, 123.75, 123.75 };
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/ModelCacheTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/ModelCacheTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/ModelCacheTest.java
deleted file mode 100644
index 7d4a020..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/ModelCacheTest.java
+++ /dev/null
@@ -1,65 +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 static org.junit.Assert.*;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class ModelCacheTest {
-
- @Test
- public void modelCacheOff() throws Exception {
- BeansWrapper bw = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
- assertFalse(bw.getUseModelCache()); // default is off
-
- String s = "foo";
- assertNotSame(bw.wrap(s), bw.wrap(s));
-
- C c = new C();
- assertNotSame(bw.wrap(c), bw.wrap(c));
- }
-
- @Test
- public void modelCacheOn() throws Exception {
- BeansWrapper bw = new BeansWrapper(Configuration.VERSION_3_0_0);
- bw.setUseModelCache(true);
- assertTrue(bw.getUseModelCache());
-
- String s = "foo";
- assertSame(bw.wrap(s), bw.wrap(s));
-
- C c = new C();
- TemplateModel wrappedC = bw.wrap(c);
- assertSame(wrappedC, bw.wrap(c));
-
- bw.clearClassIntrospecitonCache();
- assertNotSame(wrappedC, bw.wrap(c));
- assertSame(bw.wrap(c), bw.wrap(c));
- }
-
- static public class C { }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/OverloadedNumberUtilTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/OverloadedNumberUtilTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/OverloadedNumberUtilTest.java
deleted file mode 100644
index de5cc1d..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/OverloadedNumberUtilTest.java
+++ /dev/null
@@ -1,585 +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.math.BigDecimal;
-import java.math.BigInteger;
-
-import junit.framework.TestCase;
-
-@SuppressWarnings("boxing")
-public class OverloadedNumberUtilTest extends TestCase {
-
- public OverloadedNumberUtilTest(String name) {
- super(name);
- }
-
- public void testIntegerCoercions() {
- cipEqu(Byte.valueOf(Byte.MAX_VALUE));
- cipEqu(Byte.valueOf((byte) 0));
- cipEqu(Byte.valueOf(Byte.MIN_VALUE));
-
- cipEqu(Short.valueOf(Byte.MAX_VALUE),
- new OverloadedNumberUtil.ShortOrByte((short) Byte.MAX_VALUE, Byte.MAX_VALUE));
- cipEqu(Short.valueOf((short) 0),
- new OverloadedNumberUtil.ShortOrByte((short) 0, (byte) 0));
- cipEqu(Short.valueOf(Byte.MIN_VALUE),
- new OverloadedNumberUtil.ShortOrByte((short) Byte.MIN_VALUE, Byte.MIN_VALUE));
-
- cipEqu(Short.valueOf((short) (Byte.MAX_VALUE + 1)));
- cipEqu(Short.valueOf((short) (Byte.MIN_VALUE - 1)));
- cipEqu(Short.valueOf(Short.MAX_VALUE));
- cipEqu(Short.valueOf(Short.MIN_VALUE));
-
- cipEqu(Integer.valueOf(Byte.MAX_VALUE),
- new OverloadedNumberUtil.IntegerOrByte((int) Byte.MAX_VALUE, Byte.MAX_VALUE));
- cipEqu(Integer.valueOf(0),
- new OverloadedNumberUtil.IntegerOrByte(0, (byte) 0));
- cipEqu(Integer.valueOf(Byte.MIN_VALUE),
- new OverloadedNumberUtil.IntegerOrByte((int) Byte.MIN_VALUE, Byte.MIN_VALUE));
-
- cipEqu(Integer.valueOf(Byte.MAX_VALUE + 1),
- new OverloadedNumberUtil.IntegerOrShort(Byte.MAX_VALUE + 1, (short) (Byte.MAX_VALUE + 1)));
- cipEqu(Integer.valueOf(Byte.MIN_VALUE - 1),
- new OverloadedNumberUtil.IntegerOrShort(Byte.MIN_VALUE - 1, (short) (Byte.MIN_VALUE - 1)));
- cipEqu(Integer.valueOf(Short.MAX_VALUE),
- new OverloadedNumberUtil.IntegerOrShort((int) Short.MAX_VALUE, Short.MAX_VALUE));
- cipEqu(Integer.valueOf(Short.MIN_VALUE),
- new OverloadedNumberUtil.IntegerOrShort((int) Short.MIN_VALUE, Short.MIN_VALUE));
-
- cipEqu(Integer.valueOf(Short.MAX_VALUE + 1));
- cipEqu(Integer.valueOf(Short.MIN_VALUE - 1));
- cipEqu(Integer.valueOf(Integer.MAX_VALUE));
- cipEqu(Integer.valueOf(Integer.MIN_VALUE));
-
- cipEqu(Long.valueOf(Byte.MAX_VALUE),
- new OverloadedNumberUtil.LongOrByte((long) Byte.MAX_VALUE, Byte.MAX_VALUE));
- cipEqu(Long.valueOf(0),
- new OverloadedNumberUtil.LongOrByte((long) 0, (byte) 0));
- cipEqu(Long.valueOf(Byte.MIN_VALUE),
- new OverloadedNumberUtil.LongOrByte((long) Byte.MIN_VALUE, Byte.MIN_VALUE));
-
- cipEqu(Long.valueOf(Byte.MAX_VALUE + 1),
- new OverloadedNumberUtil.LongOrShort((long) (Byte.MAX_VALUE + 1), (short) (Byte.MAX_VALUE + 1)));
- cipEqu(Long.valueOf(Byte.MIN_VALUE - 1),
- new OverloadedNumberUtil.LongOrShort((long) (Byte.MIN_VALUE - 1), (short) (Byte.MIN_VALUE - 1)));
- cipEqu(Long.valueOf(Short.MAX_VALUE),
- new OverloadedNumberUtil.LongOrShort((long) Short.MAX_VALUE, Short.MAX_VALUE));
- cipEqu(Long.valueOf(Short.MIN_VALUE),
- new OverloadedNumberUtil.LongOrShort((long) Short.MIN_VALUE, Short.MIN_VALUE));
-
- cipEqu(Long.valueOf(Short.MAX_VALUE + 1),
- new OverloadedNumberUtil.LongOrInteger((long) Short.MAX_VALUE + 1, Short.MAX_VALUE + 1));
- cipEqu(Long.valueOf(Short.MIN_VALUE - 1),
- new OverloadedNumberUtil.LongOrInteger((long) Short.MIN_VALUE - 1, Short.MIN_VALUE - 1));
- cipEqu(Long.valueOf(Integer.MAX_VALUE),
- new OverloadedNumberUtil.LongOrInteger((long) Integer.MAX_VALUE, Integer.MAX_VALUE));
- cipEqu(Long.valueOf(Integer.MIN_VALUE),
- new OverloadedNumberUtil.LongOrInteger((long) Integer.MIN_VALUE, Integer.MIN_VALUE));
-
- cipEqu(Long.valueOf(Integer.MAX_VALUE + 1L));
- cipEqu(Long.valueOf(Integer.MIN_VALUE - 1L));
- cipEqu(Long.valueOf(Long.MAX_VALUE));
- cipEqu(Long.valueOf(Long.MIN_VALUE));
- }
-
- public void testIntegerNoCoercions() {
- cipEqu(Integer.valueOf(Byte.MAX_VALUE), Integer.valueOf(Byte.MAX_VALUE), 0);
- cipEqu(Integer.valueOf(0), Integer.valueOf(0), 0);
- cipEqu(Integer.valueOf(Byte.MIN_VALUE), Integer.valueOf(Byte.MIN_VALUE), 0);
- }
-
- public void testIntegerLimitedCoercions() {
- cipEqu(Integer.valueOf(Byte.MAX_VALUE), Integer.valueOf(Byte.MAX_VALUE), TypeFlags.INTEGER);
- cipEqu(Integer.valueOf(0), Integer.valueOf(0), TypeFlags.INTEGER);
- cipEqu(Integer.valueOf(Byte.MIN_VALUE), Integer.valueOf(Byte.MIN_VALUE), TypeFlags.INTEGER);
-
- cipEqu(Long.valueOf(Integer.MAX_VALUE + 1L), Long.valueOf(Integer.MAX_VALUE + 1L), TypeFlags.INTEGER);
-
- for (int n = -1; n < 2; n++) {
- final Long longN = Long.valueOf(n);
- cipEqu(longN, new OverloadedNumberUtil.LongOrInteger(longN, n), TypeFlags.INTEGER);
- cipEqu(longN, new OverloadedNumberUtil.LongOrShort(longN, (short) n), TypeFlags.SHORT);
- cipEqu(longN, new OverloadedNumberUtil.LongOrByte(longN, (byte) n), TypeFlags.BYTE);
- cipEqu(longN, new OverloadedNumberUtil.LongOrShort(longN, (short) n),
- TypeFlags.SHORT | TypeFlags.INTEGER);
- }
- }
-
- public void testBigDecimalCoercions() {
- cipEqu(new BigDecimal(123), new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal(123)));
- cipEqu(new BigDecimal(123), new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal(123)),
- TypeFlags.DOUBLE | TypeFlags.INTEGER);
- cipEqu(new BigDecimal(123), TypeFlags.INTEGER);
- cipEqu(new BigDecimal(123), TypeFlags.INTEGER | TypeFlags.LONG);
- cipEqu(new BigDecimal(123), TypeFlags.DOUBLE);
- cipEqu(new BigDecimal(123), TypeFlags.DOUBLE | TypeFlags.FLOAT);
-
- cipEqu(new BigDecimal(123.5));
- // Not wasting time with check if it's a whole number if we only have integer-only or non-integer-only targets:
- cipEqu(new BigDecimal(123.5), TypeFlags.INTEGER | TypeFlags.LONG);
- cipEqu(new BigDecimal(123.5), TypeFlags.DOUBLE | TypeFlags.FLOAT);
-
- cipEqu(new BigDecimal("0.01"));
- cipEqu(new BigDecimal("-0.01"));
- cipEqu(BigDecimal.ZERO, new OverloadedNumberUtil.IntegerBigDecimal(BigDecimal.ZERO));
- }
-
- public void testUnknownNumberCoercion() {
- cipEqu(new RationalNumber(2, 3));
- }
-
- @SuppressWarnings("boxing")
- public void testDoubleCoercion() {
- cipEqu(Double.valueOf(1.5), new OverloadedNumberUtil.DoubleOrFloat(1.5));
- cipEqu(Double.valueOf(-0.125), new OverloadedNumberUtil.DoubleOrFloat(-0.125));
- cipEqu(Double.valueOf(Float.MAX_VALUE), new OverloadedNumberUtil.DoubleOrFloat((double) Float.MAX_VALUE));
- cipEqu(Double.valueOf(-Float.MAX_VALUE), new OverloadedNumberUtil.DoubleOrFloat((double) -Float.MAX_VALUE));
- cipEqu(Double.valueOf(Float.MAX_VALUE * 10.0));
- cipEqu(Double.valueOf(-Float.MAX_VALUE * 10.0));
-
- cipEqu(Double.valueOf(0), new OverloadedNumberUtil.DoubleOrByte(0.0, (byte) 0));
- cipEqu(Double.valueOf(Byte.MAX_VALUE), new OverloadedNumberUtil.DoubleOrByte((double) Byte.MAX_VALUE, Byte.MAX_VALUE));
- cipEqu(Double.valueOf(Byte.MIN_VALUE), new OverloadedNumberUtil.DoubleOrByte((double) Byte.MIN_VALUE, Byte.MIN_VALUE));
-
- cipEqu(Double.valueOf(Byte.MAX_VALUE + 1), new OverloadedNumberUtil.DoubleOrShort((double)
- (Byte.MAX_VALUE + 1), (short) (Byte.MAX_VALUE + 1)));
- cipEqu(Double.valueOf(Byte.MIN_VALUE - 1), new OverloadedNumberUtil.DoubleOrShort((double)
- (Byte.MIN_VALUE - 1), (short) (Byte.MIN_VALUE - 1)));
-
- cipEqu(Double.valueOf(Short.MAX_VALUE + 1),
- new OverloadedNumberUtil.DoubleOrIntegerOrFloat((double) Short.MAX_VALUE + 1, Short.MAX_VALUE + 1));
- cipEqu(Double.valueOf(Short.MIN_VALUE - 1),
- new OverloadedNumberUtil.DoubleOrIntegerOrFloat((double) Short.MIN_VALUE - 1, Short.MIN_VALUE - 1));
- cipEqu(Double.valueOf(16777216), new OverloadedNumberUtil.DoubleOrIntegerOrFloat(16777216.0, 16777216));
- cipEqu(Double.valueOf(-16777216), new OverloadedNumberUtil.DoubleOrIntegerOrFloat(-16777216.0, -16777216));
-
- cipEqu(Double.valueOf(Integer.MAX_VALUE),
- new OverloadedNumberUtil.DoubleOrInteger((double) Integer.MAX_VALUE, Integer.MAX_VALUE));
- cipEqu(Double.valueOf(Integer.MIN_VALUE),
- new OverloadedNumberUtil.DoubleOrInteger((double) Integer.MIN_VALUE, Integer.MIN_VALUE));
-
- cipEqu(Double.valueOf(Integer.MAX_VALUE + 1L),
- new OverloadedNumberUtil.DoubleOrLong(Double.valueOf(Integer.MAX_VALUE + 1L), Integer.MAX_VALUE + 1L));
- cipEqu(Double.valueOf(Integer.MIN_VALUE - 1L),
- new OverloadedNumberUtil.DoubleOrLong(Double.valueOf(Integer.MIN_VALUE - 1L), Integer.MIN_VALUE - 1L));
- cipEqu(Double.valueOf(Long.MAX_VALUE),
- new OverloadedNumberUtil.DoubleOrFloat((double) Long.MAX_VALUE));
- cipEqu(Double.valueOf(Long.MIN_VALUE),
- new OverloadedNumberUtil.DoubleOrFloat((double) Long.MIN_VALUE));
-
- // When only certain target types are present:
- cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrByte(5.0, (byte) 5), TypeFlags.BYTE);
- cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrByte(5.0, (byte) 5), TypeFlags.BYTE | TypeFlags.SHORT);
- cipEqu(Double.valueOf(-129), TypeFlags.BYTE);
- cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrShort(5.0, (short) 5), TypeFlags.SHORT);
- cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrInteger(5.0, 5), TypeFlags.INTEGER);
- cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrLong(5.0, 5), TypeFlags.LONG);
- cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrFloat(5.0), TypeFlags.FLOAT);
- cipEqu(Double.valueOf(5), Double.valueOf(5), TypeFlags.DOUBLE);
- cipEqu(Double.valueOf(5), new OverloadedNumberUtil.DoubleOrFloat(5.0),
- TypeFlags.DOUBLE | TypeFlags.FLOAT);
- cipEqu(Double.valueOf(5.9), new OverloadedNumberUtil.DoubleOrFloat(5.9),
- TypeFlags.DOUBLE | TypeFlags.FLOAT);
- cipEqu(Double.valueOf(5.9),
- TypeFlags.DOUBLE | TypeFlags.INTEGER);
- cipEqu(Double.valueOf(5.9), new OverloadedNumberUtil.DoubleOrFloat(5.9),
- TypeFlags.FLOAT | TypeFlags.INTEGER);
- cipEqu(Double.valueOf(5.9), TypeFlags.INTEGER);
- cipEqu(Double.valueOf(Long.MAX_VALUE),
- new OverloadedNumberUtil.DoubleOrFloat((double) Long.MAX_VALUE),
- TypeFlags.DOUBLE | TypeFlags.FLOAT);
- cipEqu(Double.valueOf(Long.MAX_VALUE),
- TypeFlags.DOUBLE | TypeFlags.LONG);
- cipEqu(Double.valueOf(Long.MIN_VALUE),
- new OverloadedNumberUtil.DoubleOrFloat((double) Long.MIN_VALUE),
- TypeFlags.DOUBLE | TypeFlags.FLOAT);
- cipEqu(Double.valueOf(Float.MAX_VALUE * 10),
- TypeFlags.DOUBLE | TypeFlags.FLOAT);
- cipEqu(Double.valueOf(-Float.MAX_VALUE * 10),
- TypeFlags.DOUBLE | TypeFlags.FLOAT);
-
- // Rounded values:
- cipEqu(Double.valueOf(0.0000009),
- new OverloadedNumberUtil.DoubleOrByte(0.0000009, (byte) 0));
- cipEqu(Double.valueOf(-0.0000009),
- new OverloadedNumberUtil.DoubleOrByte(-0.0000009, (byte) 0));
- cipEqu(Double.valueOf(0.9999991),
- new OverloadedNumberUtil.DoubleOrByte(0.9999991, (byte) 1));
- cipEqu(Double.valueOf(-0.9999991),
- new OverloadedNumberUtil.DoubleOrByte(-0.9999991, (byte) -1));
- cipEqu(Double.valueOf(0.0000009),
- new OverloadedNumberUtil.DoubleOrShort(0.0000009, (short) 0),
- TypeFlags.SHORT | TypeFlags.DOUBLE);
- cipEqu(Double.valueOf(0.0000009), new OverloadedNumberUtil.DoubleOrInteger(0.0000009, 0),
- TypeFlags.INTEGER | TypeFlags.DOUBLE);
- cipEqu(Double.valueOf(0.0000009), new OverloadedNumberUtil.DoubleOrLong(0.0000009, 0),
- TypeFlags.LONG | TypeFlags.DOUBLE);
- cipEqu(Double.valueOf(0.0000009),
- new OverloadedNumberUtil.DoubleOrByte(0.0000009, (byte) 0), TypeFlags.BYTE);
- cipEqu(Double.valueOf(0.0000009),
- new OverloadedNumberUtil.DoubleOrShort(0.0000009, (short) 0), TypeFlags.SHORT);
- cipEqu(Double.valueOf(0.0000009),
- new OverloadedNumberUtil.DoubleOrInteger(0.0000009, 0), TypeFlags.INTEGER);
- cipEqu(Double.valueOf(0.0000009),
- new OverloadedNumberUtil.DoubleOrLong(0.0000009, 0L), TypeFlags.LONG);
- cipEqu(Double.valueOf(0.9999999),
- new OverloadedNumberUtil.DoubleOrInteger(0.9999999, 1), TypeFlags.INTEGER);
- cipEqu(Double.valueOf(Byte.MAX_VALUE + 0.9e-6),
- new OverloadedNumberUtil.DoubleOrByte(Byte.MAX_VALUE + 0.9e-6, Byte.MAX_VALUE));
- cipEqu(Double.valueOf(Byte.MIN_VALUE - 0.9e-6),
- new OverloadedNumberUtil.DoubleOrByte(Byte.MIN_VALUE - 0.9e-6, Byte.MIN_VALUE));
- cipEqu(Double.valueOf(Byte.MAX_VALUE + 1.1e-6),
- new OverloadedNumberUtil.DoubleOrFloat(Byte.MAX_VALUE + 1.1e-6));
- cipEqu(Double.valueOf(Byte.MIN_VALUE - 1.1e-6),
- new OverloadedNumberUtil.DoubleOrFloat(Byte.MIN_VALUE - 1.1e-6));
- cipEqu(Double.valueOf(Byte.MAX_VALUE + 0.9999991),
- new OverloadedNumberUtil.DoubleOrShort(
- Byte.MAX_VALUE + 0.9999991, (short) (Byte.MAX_VALUE + 1)));
- cipEqu(Double.valueOf(Byte.MIN_VALUE - 0.9999991),
- new OverloadedNumberUtil.DoubleOrShort(
- Byte.MIN_VALUE - 0.9999991, (short) (Byte.MIN_VALUE - 1)));
-
- cipEqu(Double.valueOf(Byte.MAX_VALUE + 1), new OverloadedNumberUtil.DoubleOrShort((double)
- (Byte.MAX_VALUE + 1), (short) (Byte.MAX_VALUE + 1)));
- cipEqu(Double.valueOf(Byte.MIN_VALUE - 1), new OverloadedNumberUtil.DoubleOrShort((double)
- (Byte.MIN_VALUE - 1), (short) (Byte.MIN_VALUE - 1)));
-
- cipEqu(Short.MAX_VALUE + 0.9999991,
- new OverloadedNumberUtil.DoubleOrIntegerOrFloat(Short.MAX_VALUE + 0.9999991, Short.MAX_VALUE + 1));
- cipEqu(Short.MIN_VALUE - 0.9999991,
- new OverloadedNumberUtil.DoubleOrIntegerOrFloat(Short.MIN_VALUE - 0.9999991, Short.MIN_VALUE - 1));
- cipEqu(16777216 + 0.9e-6,
- new OverloadedNumberUtil.DoubleOrIntegerOrFloat(16777216 + 0.9e-6, 16777216));
- cipEqu(-16777216 - 0.9e-6,
- new OverloadedNumberUtil.DoubleOrIntegerOrFloat(-16777216 - 0.9e-6, -16777216));
-
- cipEqu(Integer.MAX_VALUE + 0.9e-6,
- new OverloadedNumberUtil.DoubleOrInteger(Integer.MAX_VALUE + 0.9e-6, Integer.MAX_VALUE));
- cipEqu(Integer.MIN_VALUE - 0.9e-6,
- new OverloadedNumberUtil.DoubleOrInteger(Integer.MIN_VALUE - 0.9e-6, Integer.MIN_VALUE));
-
- cipEqu(Integer.MAX_VALUE + 1L + 0.9e-6,
- new OverloadedNumberUtil.DoubleOrFloat(Integer.MAX_VALUE + 1L + 0.9e-6));
- cipEqu(Integer.MIN_VALUE - 1L - 0.9e-6,
- new OverloadedNumberUtil.DoubleOrFloat(Integer.MIN_VALUE - 1L - 0.9e-6));
- cipEqu(Long.MAX_VALUE + 0.9e-6,
- new OverloadedNumberUtil.DoubleOrFloat(Long.MAX_VALUE + 0.9e-6));
- cipEqu(Long.MIN_VALUE - 0.9e-6,
- new OverloadedNumberUtil.DoubleOrFloat(Long.MIN_VALUE - 0.9e-6));
- }
-
- @SuppressWarnings("boxing")
- public void testFloatCoercion() {
- cipEqu(1.00002f);
- cipEqu(-1.00002f);
- cipEqu(1.999989f);
- cipEqu(-1.999989f);
- cipEqu(16777218f);
- cipEqu(-16777218f);
-
- cipEqu(1f, new OverloadedNumberUtil.FloatOrByte(1f, (byte) 1));
- cipEqu(-1f, new OverloadedNumberUtil.FloatOrByte(-1f, (byte) -1));
- cipEqu(1.000009f, new OverloadedNumberUtil.FloatOrByte(1.000009f, (byte) 1));
- cipEqu(-1.000009f, new OverloadedNumberUtil.FloatOrByte(-1.000009f, (byte) -1));
- cipEqu(1.999991f, new OverloadedNumberUtil.FloatOrByte(1.999991f, (byte) 2));
- cipEqu(-1.999991f, new OverloadedNumberUtil.FloatOrByte(-1.999991f, (byte) -2));
-
- cipEqu(1000f, new OverloadedNumberUtil.FloatOrShort(1000f, (short) 1000));
- cipEqu(-1000f, new OverloadedNumberUtil.FloatOrShort(-1000f, (short) -1000));
- cipEqu(1000.00006f);
-
- cipEqu(60000f, new OverloadedNumberUtil.FloatOrInteger(60000f, 60000));
- cipEqu(-60000f, new OverloadedNumberUtil.FloatOrInteger(-60000f, -60000));
- cipEqu(60000.004f);
-
- cipEqu(100f, new OverloadedNumberUtil.FloatOrByte(100f, (byte) 100), TypeFlags.MASK_KNOWN_INTEGERS);
- cipEqu(1000f, new OverloadedNumberUtil.FloatOrShort(1000f, (short) 1000), TypeFlags.MASK_KNOWN_INTEGERS);
- cipEqu(60000f, new OverloadedNumberUtil.FloatOrInteger(60000f, 60000), TypeFlags.MASK_KNOWN_INTEGERS);
- cipEqu(60000f, new OverloadedNumberUtil.FloatOrInteger(60000f, 60000), TypeFlags.LONG);
- cipEqu((float) Integer.MAX_VALUE, (float) Integer.MAX_VALUE, TypeFlags.LONG);
- cipEqu((float) -Integer.MAX_VALUE, (float) -Integer.MAX_VALUE);
-
- cipEqu(0.5f, 0.5f, TypeFlags.DOUBLE | TypeFlags.FLOAT);
- cipEqu(0.5f, 0.5f, TypeFlags.DOUBLE);
- }
-
- public void testBigIntegerCoercion() {
- BigInteger bi;
-
- cipEqu(BigInteger.ZERO, new OverloadedNumberUtil.BigIntegerOrByte(BigInteger.ZERO));
- bi = new BigInteger(String.valueOf(Byte.MAX_VALUE));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrByte(bi));
- bi = new BigInteger(String.valueOf(Byte.MIN_VALUE));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrByte(bi));
-
- bi = new BigInteger(String.valueOf(Byte.MAX_VALUE + 1));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi));
- bi = new BigInteger(String.valueOf(Byte.MIN_VALUE - 1));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi));
- bi = new BigInteger(String.valueOf(Short.MAX_VALUE));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi));
- bi = new BigInteger(String.valueOf(Short.MIN_VALUE));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrShort(bi));
-
- bi = new BigInteger(String.valueOf(Short.MAX_VALUE + 1));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi));
- bi = new BigInteger(String.valueOf(Short.MIN_VALUE - 1));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi));
- bi = new BigInteger(String.valueOf(Integer.MAX_VALUE));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi));
- bi = new BigInteger(String.valueOf(Integer.MIN_VALUE));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrInteger(bi));
-
- bi = new BigInteger(String.valueOf(Integer.MAX_VALUE + 1L));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi));
- bi = new BigInteger(String.valueOf(Integer.MIN_VALUE - 1L));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi));
- bi = new BigInteger(String.valueOf(Long.MAX_VALUE));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi));
- bi = new BigInteger(String.valueOf(Long.MIN_VALUE));
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrLong(bi));
-
- cipEqu(new BigInteger(String.valueOf(Long.MAX_VALUE)).add(BigInteger.ONE));
- cipEqu(new BigInteger(String.valueOf(Long.MIN_VALUE)).subtract(BigInteger.ONE));
-
- bi = new BigInteger("0");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi),
- TypeFlags.DOUBLE | TypeFlags.FLOAT);
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi),
- TypeFlags.MASK_KNOWN_NONINTEGERS);
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi),
- TypeFlags.FLOAT);
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi),
- TypeFlags.DOUBLE);
-
- bi = new BigInteger("16777215");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
- bi = new BigInteger("-16777215");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
-
- bi = new BigInteger("16777216");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
- bi = new BigInteger("-16777216");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrFloat(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
-
- bi = new BigInteger("16777217");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
- cipEqu(bi, TypeFlags.FLOAT);
- bi = new BigInteger("-16777217");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
- cipEqu(bi, TypeFlags.FLOAT);
-
- bi = new BigInteger("9007199254740991");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE);
- bi = new BigInteger("-9007199254740991");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE);
-
- bi = new BigInteger("9007199254740992");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE);
- bi = new BigInteger("-9007199254740992");
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.MASK_KNOWN_NONINTEGERS);
- cipEqu(bi, new OverloadedNumberUtil.BigIntegerOrDouble(bi), TypeFlags.DOUBLE);
-
- bi = new BigInteger("9007199254740993");
- cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS);
- cipEqu(bi, TypeFlags.DOUBLE | TypeFlags.FLOAT);
- cipEqu(bi, TypeFlags.FLOAT);
- cipEqu(bi, TypeFlags.DOUBLE);
- bi = new BigInteger("-9007199254740993");
- cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS);
- cipEqu(bi, TypeFlags.DOUBLE | TypeFlags.FLOAT);
- cipEqu(bi, TypeFlags.FLOAT);
- cipEqu(bi, TypeFlags.DOUBLE);
-
- bi = new BigInteger("9007199254740994");
- cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS);
- bi = new BigInteger("-9007199254740994");
- cipEqu(bi, TypeFlags.MASK_KNOWN_NONINTEGERS);
- }
-
- private void cipEqu(Number actualAndExpected) {
- cipEqu(actualAndExpected, actualAndExpected, -1);
- }
-
- private void cipEqu(Number actual, Number expected) {
- cipEqu(actual, expected, -1);
- }
-
- private void cipEqu(Number actualAndExpected, int flags) {
- cipEqu(actualAndExpected, actualAndExpected, flags);
- }
-
- @SuppressWarnings("boxing")
- private void cipEqu(Number actual, Number expected, int flags) {
- Number res = OverloadedNumberUtil.addFallbackType(actual, flags);
- assertEquals(expected.getClass(), res.getClass());
- assertEquals(expected, res);
-
- // Some number types wrap the number with multiple types and equals() only compares one of them. So we try to
- // catch any inconsistency:
- assertEquals(expected.byteValue(), res.byteValue());
- assertEquals(expected.shortValue(), res.shortValue());
- assertEquals(expected.intValue(), res.intValue());
- assertEquals(expected.longValue(), res.longValue());
- assertEquals(expected.floatValue(), res.floatValue());
- assertEquals(expected.doubleValue(), res.doubleValue());
- }
-
- public void testGetArgumentConversionPrice() {
- // Unknown number types:
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- RationalNumber.class, Integer.class));
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, RationalNumber.class));
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- RationalNumber.class, Float.class));
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- Float.class, RationalNumber.class));
- assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
- RationalNumber.class, RationalNumber.class));
-
- // Fully check some rows (not all of them; the code is generated anyways):
-
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, Byte.class));
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, Short.class));
- assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, Integer.class));
- assertEquals(10004, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, Long.class));
- assertEquals(10005, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, BigInteger.class));
- assertEquals(30006, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, Float.class));
- assertEquals(20007, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, Double.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, BigDecimal.class));
-
- assertEquals(45001, OverloadedNumberUtil.getArgumentConversionPrice(
- BigDecimal.class, Byte.class));
- assertEquals(44002, OverloadedNumberUtil.getArgumentConversionPrice(
- BigDecimal.class, Short.class));
- assertEquals(41003, OverloadedNumberUtil.getArgumentConversionPrice(
- BigDecimal.class, Integer.class));
- assertEquals(41004, OverloadedNumberUtil.getArgumentConversionPrice(
- BigDecimal.class, Long.class));
- assertEquals(40005, OverloadedNumberUtil.getArgumentConversionPrice(
- BigDecimal.class, BigInteger.class));
- assertEquals(33006, OverloadedNumberUtil.getArgumentConversionPrice(
- BigDecimal.class, Float.class));
- assertEquals(32007, OverloadedNumberUtil.getArgumentConversionPrice(
- BigDecimal.class, Double.class));
- assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
- BigDecimal.class, BigDecimal.class));
-
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrLong.class, Byte.class));
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrLong.class, Short.class));
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrLong.class, Integer.class));
- assertEquals(21004, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrLong.class, Long.class));
- assertEquals(21005, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrLong.class, BigInteger.class));
- assertEquals(40006, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrLong.class, Float.class));
- assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrLong.class, Double.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrLong.class, BigDecimal.class));
-
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrDouble.class, Byte.class));
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrDouble.class, Short.class));
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrDouble.class, Integer.class));
- assertEquals(Integer.MAX_VALUE, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrDouble.class, Long.class));
- assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrDouble.class, BigInteger.class));
- assertEquals(40006, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrDouble.class, Float.class));
- assertEquals(20007, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrDouble.class, Double.class));
- assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrDouble.class, BigDecimal.class));
-
- // Check if all fromC is present:
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- Byte.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- Short.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- Integer.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- Long.class, BigDecimal.class));
- assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
- BigInteger.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- Float.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- Double.class, BigDecimal.class));
- assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
- BigDecimal.class, BigDecimal.class));
- assertEquals(0, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.IntegerBigDecimal.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrFloat.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.FloatOrByte.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrShort.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.FloatOrByte.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.FloatOrShort.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.FloatOrInteger.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrByte.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrIntegerOrFloat.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrInteger.class, BigDecimal.class));
- assertEquals(20008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.DoubleOrLong.class, BigDecimal.class));
- assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrByte.class, BigDecimal.class));
- assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrShort.class, BigDecimal.class));
- assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrInteger.class, BigDecimal.class));
- assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrLong.class, BigDecimal.class));
- assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrFloat.class, BigDecimal.class));
- assertEquals(10008, OverloadedNumberUtil.getArgumentConversionPrice(
- OverloadedNumberUtil.BigIntegerOrDouble.class, BigDecimal.class));
- }
-
-}
[02/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/ParameterListPreferabilityTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/ParameterListPreferabilityTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/ParameterListPreferabilityTest.java
deleted file mode 100644
index 60c07ef..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/ParameterListPreferabilityTest.java
+++ /dev/null
@@ -1,445 +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.io.Serializable;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.apache.freemarker.core.util._NumberUtil;
-
-import junit.framework.TestCase;
-
-@SuppressWarnings("boxing")
-public class ParameterListPreferabilityTest extends TestCase {
-
- public ParameterListPreferabilityTest(String name) {
- super(name);
- }
-
- public void testNumberical() {
- // Note: the signature lists consists of the same elements, only their order changes depending on the type
- // of the argument value.
-
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { byte.class },
- new Class[] { Byte.class },
- new Class[] { short.class },
- new Class[] { Short.class },
- new Class[] { int.class },
- new Class[] { Integer.class },
- new Class[] { long.class },
- new Class[] { Long.class },
- new Class[] { BigInteger.class },
- new Class[] { float.class },
- new Class[] { Float.class },
- new Class[] { double.class },
- new Class[] { Double.class },
- new Class[] { BigDecimal.class },
- new Class[] { Number.class },
- new Class[] { Serializable.class },
- new Class[] { Object.class }
- },
- new Object[] { (byte) 1 });
-
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { BigDecimal.class },
- new Class[] { BigInteger.class },
- new Class[] { int.class },
- new Class[] { Integer.class },
- new Class[] { long.class },
- new Class[] { Long.class },
- new Class[] { double.class },
- new Class[] { Double.class },
- new Class[] { float.class },
- new Class[] { Float.class },
- new Class[] { short.class },
- new Class[] { Short.class },
- new Class[] { byte.class },
- new Class[] { Byte.class },
- new Class[] { Number.class },
- new Class[] { Serializable.class },
- new Class[] { Object.class },
- },
- new Object[] { new OverloadedNumberUtil.IntegerBigDecimal(new BigDecimal("1")) });
-
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { BigDecimal.class },
- new Class[] { double.class },
- new Class[] { Double.class },
- new Class[] { float.class },
- new Class[] { Float.class },
- new Class[] { BigInteger.class },
- new Class[] { int.class },
- new Class[] { Integer.class },
- new Class[] { long.class },
- new Class[] { Long.class },
- new Class[] { short.class },
- new Class[] { Short.class },
- new Class[] { byte.class },
- new Class[] { Byte.class },
- new Class[] { Number.class },
- new Class[] { Serializable.class },
- new Class[] { Object.class },
- },
- new Object[] { new BigDecimal("1") /* possibly non-integer */ });
-
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { float.class },
- new Class[] { Float.class },
- new Class[] { double.class },
- new Class[] { Double.class },
- new Class[] { BigDecimal.class },
- new Class[] { int.class },
- new Class[] { Integer.class },
- new Class[] { long.class },
- new Class[] { Long.class },
- new Class[] { short.class },
- new Class[] { Short.class },
- new Class[] { byte.class },
- new Class[] { Byte.class },
- new Class[] { BigInteger.class },
- new Class[] { Number.class },
- new Class[] { Serializable.class },
- new Class[] { Object.class },
- },
- new Object[] { new OverloadedNumberUtil.FloatOrByte(1f, (byte) 1) });
-
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { int.class },
- new Class[] { Integer.class },
- new Class[] { long.class },
- new Class[] { Long.class },
- new Class[] { BigInteger.class },
- new Class[] { double.class },
- new Class[] { Double.class },
- new Class[] { BigDecimal.class },
- new Class[] { short.class },
- new Class[] { Short.class },
- new Class[] { float.class },
- new Class[] { Float.class },
-
- // Two incompatibles removed, would be removed in reality:
- new Class[] { byte.class },
- new Class[] { Byte.class },
-
- new Class[] { Number.class },
- new Class[] { Serializable.class },
- new Class[] { Object.class }
- },
- new Object[] { new OverloadedNumberUtil.IntegerOrShort(1, (short) 1) });
-
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { long.class },
- new Class[] { Long.class },
- new Class[] { BigInteger.class },
- new Class[] { BigDecimal.class },
- new Class[] { double.class },
- new Class[] { Double.class },
- new Class[] { float.class },
- new Class[] { Float.class },
- // skip byte and short as the would be equal with int (all invalid target types)
- new Class[] { int.class }, // In reality, this and Integer are removed as not-applicable overloads
- new Class[] { Integer.class },
- new Class[] { Number.class },
- new Class[] { Serializable.class },
- new Class[] { Object.class }
- },
- new Object[] { 1L });
-
- // Undecidable comparisons:
-
- testAllCmpPermutationsEqu(
- new Class[][] {
- new Class[] { Byte.class },
- new Class[] { Short.class },
- new Class[] { Integer.class },
- new Class[] { Long.class },
- new Class[] { BigInteger.class },
- new Class[] { Float.class },
- },
- new Object[] { 1.0 });
-
- testAllCmpPermutationsEqu(
- new Class[][] {
- new Class[] { byte.class },
- new Class[] { short.class },
- new Class[] { int.class },
- new Class[] { long.class },
- new Class[] { float.class },
- },
- new Object[] { 1.0 });
- }
-
- public void testPrimitiveIsMoreSpecific() {
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { boolean.class },
- new Class[] { Boolean.class }
- },
- new Object[] { true });
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { char.class },
- new Class[] { Character.class }
- },
- new Object[] { 'x' });
- }
-
- public void testCharIsMoreSpecificThanString() {
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { char.class },
- new Class[] { Character.class },
- new Class[] { String.class },
- new Class[] { CharSequence.class }
- },
- new Object[] { "s" });
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { char.class },
- new Class[] { Character.class },
- new Class[] { String.class }
- },
- new Object[] { 'c' });
- }
-
- public void testClassHierarchy() {
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { LinkedHashMap.class },
- new Class[] { HashMap.class },
- new Class[] { Map.class },
- new Class[] { Object.class }
- },
- new Object[] { new LinkedHashMap() });
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { LinkedHashMap.class },
- new Class[] { Cloneable.class },
- new Class[] { Object.class }
- },
- new Object[] { new LinkedHashMap() });
- }
-
- public void testNumericalWithNonNumerical() {
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { int.class },
- new Class[] { Integer.class },
- new Class[] { Comparable.class },
- new Class[] { Object.class },
- },
- new Object[] { 1 });
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { int.class },
- new Class[] { Integer.class },
- new Class[] { char.class },
- new Class[] { Character.class },
- },
- new Object[] { 1 });
- }
-
- public void testUnrelated() {
- testAllCmpPermutationsEqu(
- new Class[][] {
- new Class[] { Serializable.class },
- new Class[] { CharSequence.class },
- new Class[] { Comparable.class }
- },
- new Object[] { "s" });
-
- testAllCmpPermutationsEqu(
- new Class[][] {
- new Class[] { HashMap.class },
- new Class[] { TreeMap.class }
- },
- new Object[] { null });
- }
-
- public void testMultiParameter() {
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { String.class, String.class, String.class },
-
- new Class[] { String.class, String.class, Object.class },
- new Class[] { String.class, Object.class, String.class },
- new Class[] { Object.class, String.class, String.class },
-
- new Class[] { String.class, Object.class, Object.class },
- new Class[] { Object.class, String.class, Object.class },
- new Class[] { Object.class, Object.class, String.class },
-
- new Class[] { Object.class, Object.class, Object.class },
- },
- new Object[] { "a", "b", "c" });
-
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { String.class, String.class },
- new Class[] { String.class, Object.class },
- new Class[] { CharSequence.class, CharSequence.class },
- new Class[] { CharSequence.class, Object.class },
- new Class[] { Object.class, String.class },
- new Class[] { Object.class, CharSequence.class },
- new Class[] { Object.class, Object.class },
- },
- new Object[] { "a", "b" });
-
- // Subclassing is more important than primitive-VS-boxed:
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { boolean.class, boolean.class, boolean.class, String.class },
- new Class[] { Boolean.class, boolean.class, boolean.class, String.class },
- new Class[] { boolean.class, Boolean.class, Boolean.class, String.class },
- new Class[] { Boolean.class, boolean.class, Boolean.class, String.class },
- new Class[] { Boolean.class, Boolean.class, boolean.class, String.class },
- new Class[] { Boolean.class, Boolean.class, Boolean.class, String.class },
- new Class[] { Boolean.class, Boolean.class, Boolean.class, CharSequence.class },
- new Class[] { boolean.class, boolean.class, boolean.class, Object.class },
- new Class[] { Boolean.class, boolean.class, boolean.class, Object.class },
- new Class[] { boolean.class, Boolean.class, Boolean.class, Object.class },
- new Class[] { Boolean.class, boolean.class, Boolean.class, Object.class },
- new Class[] { Boolean.class, Boolean.class, boolean.class, Object.class },
- new Class[] { Boolean.class, Boolean.class, Boolean.class, Object.class },
- },
- new Object[] { true, false, true, "a" });
-
- // Subclassing is more important than primitive-VS-boxed:
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { int.class, int.class, int.class, String.class },
- new Class[] { Integer.class, int.class, int.class, String.class },
- new Class[] { int.class, Integer.class, Integer.class, String.class },
- new Class[] { Integer.class, int.class, Integer.class, String.class },
- new Class[] { Integer.class, Integer.class, int.class, String.class },
- new Class[] { Integer.class, Integer.class, Integer.class, String.class },
- new Class[] { Integer.class, Integer.class, Integer.class, CharSequence.class },
- new Class[] { int.class, int.class, int.class, Object.class },
- new Class[] { Integer.class, int.class, int.class, Object.class },
- new Class[] { int.class, Integer.class, Integer.class, Object.class },
- new Class[] { Integer.class, int.class, Integer.class, Object.class },
- new Class[] { Integer.class, Integer.class, int.class, Object.class },
- new Class[] { Integer.class, Integer.class, Integer.class, Object.class },
- },
- new Object[] { 1, 2, 3, "a" });
- }
-
- public void testVarargs() {
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { String.class, String[].class },
- new Class[] { String.class, CharSequence[].class },
- new Class[] { String.class, Object[].class },
- },
- new Object[] { "a", "b", "c" },
- true);
-
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { String.class, int[].class },
- new Class[] { String.class, Integer[].class },
- new Class[] { String.class, long[].class },
- new Class[] { String.class, Long[].class },
- new Class[] { String.class, double[].class },
- new Class[] { String.class, Double[].class },
- new Class[] { String.class, Serializable[].class },
- new Class[] { String.class, Object[].class },
- },
- new Object[] { "a", 1, 2, 3 },
- true);
-
- // 0-long varargs list; in case of ambiguity, the varargs component type decides:
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { String.class, Object[].class },
- new Class[] { CharSequence.class, int[].class },
- new Class[] { CharSequence.class, Integer[].class },
- new Class[] { CharSequence.class, long[].class },
- new Class[] { CharSequence.class, Long[].class },
- new Class[] { CharSequence.class, double[].class },
- new Class[] { CharSequence.class, Double[].class },
- new Class[] { CharSequence.class, Serializable[].class },
- new Class[] { CharSequence.class, Object[].class },
- new Class[] { Object.class, int[].class },
- },
- new Object[] { "a" },
- true);
-
-
- // Different oms prefix length; in the case of ambiguity, the one with higher oms param count wins.
- testAllCmpPermutationsInc(
- new Class[][] {
- new Class[] { String.class, int.class, int.class, int[].class },
- new Class[] { String.class, int.class, int[].class },
- new Class[] { String.class, int[].class },
- },
- new Object[] { "a", 1, 2, 3 },
- true);
- }
-
- private void testAllCmpPermutationsInc(Class[][] sortedSignatures, Object[] args) {
- testAllCmpPermutationsInc(sortedSignatures, args, false);
- }
-
- /**
- * Compares all items with all other items in the provided descending sorted array of signatures, checking that
- * for all valid indexes i and j, where j > i, it stands that sortedSignatures[i] > sortedSignatures[j].
- * The comparisons are done with both operand orders, also each items is compared to itself too.
- *
- * @param sortedSignatures method signatures sorted by decreasing specificity
- */
- private void testAllCmpPermutationsInc(Class[][] sortedSignatures, Object[] args, boolean varargs) {
- final ArgumentTypes argTs = new ArgumentTypes(args);
- for (int i = 0; i < sortedSignatures.length; i++) {
- for (int j = 0; j < sortedSignatures.length; j++) {
- assertEquals("sortedSignatures[" + i + "] <==> sortedSignatures [" + j + "]",
- _NumberUtil.getSignum(
- Integer.valueOf(j).compareTo(i)),
- _NumberUtil.getSignum(
- argTs.compareParameterListPreferability(
- sortedSignatures[i], sortedSignatures[j], varargs)));
- }
- }
- }
-
- private void testAllCmpPermutationsEqu(Class[][] signatures, Object[] args) {
- final ArgumentTypes argTs = new ArgumentTypes(args);
- for (int i = 0; i < signatures.length; i++) {
- for (int j = 0; j < signatures.length; j++) {
- assertEquals("sortedSignatures[" + i + "] <==> sortedSignatures [" + j + "]",
- 0,
- argTs.compareParameterListPreferability(signatures[i], signatures[j], false));
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/PrallelObjectIntrospectionTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/PrallelObjectIntrospectionTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/PrallelObjectIntrospectionTest.java
deleted file mode 100644
index de84173..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/PrallelObjectIntrospectionTest.java
+++ /dev/null
@@ -1,43 +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 org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-public class PrallelObjectIntrospectionTest extends AbstractParallelIntrospectionTest {
-
- public PrallelObjectIntrospectionTest(String name) {
- super(name);
- }
-
- public static void main(String[] args) {
- new PrallelObjectIntrospectionTest("almostForever")
- .testReliability(Integer.MAX_VALUE);
- }
-
- @Override
- protected TemplateHashModel getWrappedEntity(int objIdx)
- throws TemplateModelException {
- return (TemplateHashModel) getBeansWrapper().wrap(
- ManyObjectsOfDifferentClasses.OBJECTS[objIdx]);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/PrallelStaticIntrospectionTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/PrallelStaticIntrospectionTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/PrallelStaticIntrospectionTest.java
deleted file mode 100644
index a47d0e4..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/PrallelStaticIntrospectionTest.java
+++ /dev/null
@@ -1,47 +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 org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-public class PrallelStaticIntrospectionTest extends AbstractParallelIntrospectionTest {
-
- private static final String STATICS_CLASS_CONTAINER_CLASS_NAME
- = ManyStaticsOfDifferentClasses.class.getName();
-
- public PrallelStaticIntrospectionTest(String name) {
- super(name);
- }
-
- public static void main(String[] args) {
- new PrallelStaticIntrospectionTest("almostForever")
- .testReliability(Integer.MAX_VALUE);
- }
-
- @Override
- protected TemplateHashModel getWrappedEntity(int clIdx)
- throws TemplateModelException {
- return (TemplateHashModel) getBeansWrapper().getStaticModels().get(
- STATICS_CLASS_CONTAINER_CLASS_NAME + "$C"
- + clIdx);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/RationalNumber.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/RationalNumber.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/RationalNumber.java
deleted file mode 100644
index 8e05262..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/RationalNumber.java
+++ /dev/null
@@ -1,90 +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;
-
-public final class RationalNumber extends Number {
-
- final int divident;
- final int divisor;
-
- public RationalNumber(int divident, int divisor) {
- this.divident = divident;
- this.divisor = divisor;
- }
-
- @Override
- public int intValue() {
- return divident / divisor;
- }
-
- @Override
- public long longValue() {
- return divident / (long) divisor;
- }
-
- @Override
- public float floatValue() {
- return (float) (divident / (double) divisor);
- }
-
- @Override
- public double doubleValue() {
- return divident / (double) divisor;
- }
-
- public int getDivident() {
- return divident;
- }
-
- public int getDivisor() {
- return divisor;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + divident;
- result = prime * result + divisor;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- RationalNumber other = (RationalNumber) obj;
- if (divident != other.divident)
- return false;
- if (divisor != other.divisor)
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return divident + "/" + divisor;
- }
-
-}
\ 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/beans/StaticModelsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/StaticModelsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/StaticModelsTest.java
deleted file mode 100644
index 35e9e7a..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/StaticModelsTest.java
+++ /dev/null
@@ -1,90 +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 static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.util.ArrayList;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class StaticModelsTest {
-
- @Test
- public void modelCaching() throws Exception {
- BeansWrapper bw = new BeansWrapper(Configuration.VERSION_3_0_0);
- TemplateHashModel statics = bw.getStaticModels();
- TemplateHashModel s = (TemplateHashModel) statics.get(S.class.getName());
- assertNotNull(s);
- assertNotNull(s.get("F"));
- assertNotNull(s.get("m"));
- try {
- s.get("x");
- fail();
- } catch (TemplateModelException e) {
- assertThat(e.getMessage(), containsString("No such key"));
- }
-
- try {
- statics.get("no.such.ClassExists");
- fail();
- } catch (TemplateModelException e) {
- assertTrue(e.getCause() instanceof ClassNotFoundException);
- }
-
- TemplateModel f = s.get("F");
- assertTrue(f instanceof TemplateScalarModel);
- assertEquals(((TemplateScalarModel) f).getAsString(), "F OK");
-
- TemplateModel m = s.get("m");
- assertTrue(m instanceof TemplateMethodModelEx);
- assertEquals(((TemplateScalarModel) ((TemplateMethodModelEx) m).exec(new ArrayList())).getAsString(), "m OK");
-
- assertSame(s, statics.get(S.class.getName()));
-
- bw.clearClassIntrospecitonCache();
- TemplateHashModel sAfterClean = (TemplateHashModel) statics.get(S.class.getName());
- assertNotSame(s, sAfterClean);
- assertSame(sAfterClean, statics.get(S.class.getName()));
- assertNotNull(sAfterClean.get("F"));
- assertNotNull(sAfterClean.get("m"));
- }
-
- public static class S {
-
- public static final String F = "F OK";
-
- public static String m() {
- return "m OK";
- }
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/beans/TypeFlagsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/beans/TypeFlagsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/beans/TypeFlagsTest.java
deleted file mode 100644
index 301e08f..0000000
--- a/src/test/java/org/apache/freemarker/core/model/impl/beans/TypeFlagsTest.java
+++ /dev/null
@@ -1,671 +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.io.File;
-import java.lang.reflect.Method;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.freemarker.core.Configuration;
-
-import junit.framework.TestCase;
-
-public class TypeFlagsTest extends TestCase {
-
- public TypeFlagsTest(String name) {
- super(name);
- }
-
- private final BeansWrapper bw = new BeansWrapper(Configuration.VERSION_3_0_0);
-
- public void testSingleNumType() {
- checkTypeFlags(SingleNumTypeC.class, "mInt",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.INTEGER | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- | TypeFlags.ACCEPTS_STRING);
- checkTypeFlags(SingleNumTypeC.class, "mLong",
- TypeFlags.LONG | TypeFlags.ACCEPTS_NUMBER);
- checkTypeFlags(SingleNumTypeC.class, "mShort",
- TypeFlags.SHORT | TypeFlags.ACCEPTS_NUMBER);
- checkTypeFlags(SingleNumTypeC.class, "mByte",
- TypeFlags.BYTE | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.ACCEPTS_ANY_OBJECT);
- checkTypeFlags(SingleNumTypeC.class, "mDouble",
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER);
- checkTypeFlags(SingleNumTypeC.class, "mFloat",
- TypeFlags.FLOAT | TypeFlags.ACCEPTS_NUMBER);
- checkTypeFlags(SingleNumTypeC.class, "mUnknown",
- TypeFlags.UNKNOWN_NUMERICAL_TYPE | TypeFlags.ACCEPTS_NUMBER);
-
- checkTypeFlags(SingleNumTypeC.class, "mVarParamCnt",
- TypeFlags.BIG_DECIMAL | TypeFlags.ACCEPTS_NUMBER);
- checkTypeFlags(SingleNumTypeC.class, "mVarParamCnt",
- TypeFlags.BIG_INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER);
- checkTypeFlags(SingleNumTypeC.class, "mVarParamCnt",
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.FLOAT | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER);
- }
-
- static public class SingleNumTypeC {
- public void mInt(int a1, String a2) { }
- public void mInt(int a1, int a2) { }
- public void mLong(long a1) { }
- public void mLong(Long a1) { }
- public void mShort(short a1) { }
- public void mByte(byte a1, boolean a2) { }
- public void mByte(byte a1, String a2) { }
- public void mByte(byte a1, Object a2) { }
- public void mDouble(double a1) { }
- public void mFloat(float a1) { }
- public void mUnknown(RationalNumber a1) { };
-
- public void mVarParamCnt(BigDecimal a1) { }
- public void mVarParamCnt(BigInteger a1, Double a2) { }
- public void mVarParamCnt(Double a1, Float a2, Integer a3) { }
- public void mVarParamCnt(Object a1, char a2, boolean a3, File a4, Map a5, Boolean a6) { }
- public void mVarParamCnt(Long a1, int a2, short a3, byte a4, double a5, float a6) { }
- }
-
- public void testMultipleNumTypes() {
- checkTypeFlags(MultiNumTypeC.class, "m1",
- TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
- | TypeFlags.BYTE | TypeFlags.DOUBLE | TypeFlags.INTEGER
- | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(MultiNumTypeC.class, "m2",
- TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
- | TypeFlags.SHORT | TypeFlags.LONG | TypeFlags.FLOAT
- | TypeFlags.ACCEPTS_NUMBER | TypeFlags.CHARACTER
- );
-
- checkTypeFlags(MultiNumTypeC.class, "m3",
- TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
- | TypeFlags.BIG_DECIMAL | TypeFlags.BIG_INTEGER
- | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.BIG_INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
- | TypeFlags.BIG_DECIMAL | TypeFlags.UNKNOWN_NUMERICAL_TYPE
- | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(MultiNumTypeC.class, "m4",
- TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.FLOAT | TypeFlags.ACCEPTS_NUMBER
- | TypeFlags.CHARACTER
- );
-
- checkTypeFlags(MultiNumTypeC.class, "m5",
- TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
- | TypeFlags.FLOAT | TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(MultiNumTypeC.class, "m6",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER
- );
- assertSame(getTypeFlags(MultiNumTypeC.class, "m6", false, 2), OverloadedMethodsSubset.ALL_ZEROS_ARRAY);
- assertSame(getTypeFlags(MultiNumTypeC.class, "m6", true, 2), OverloadedMethodsSubset.ALL_ZEROS_ARRAY);
- assertSame(getTypeFlags(MultiNumTypeC.class, "m6", false, 3), OverloadedMethodsSubset.ALL_ZEROS_ARRAY);
- assertSame(getTypeFlags(MultiNumTypeC.class, "m6", true, 3), OverloadedMethodsSubset.ALL_ZEROS_ARRAY);
- checkTypeFlags(MultiNumTypeC.class, "m6",
- TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
- 0
- );
- }
-
- static public class MultiNumTypeC {
- public void m1(byte a1) { };
- public void m1(int a1) { };
- public void m1(double a2) { };
-
- public void m2(short a1) { };
- public void m2(long a1) { };
- public void m2(float a1) { };
- public void m2(char a1) { };
-
- public void m3(BigInteger a1, BigInteger a2, BigDecimal a3) { };
- public void m3(BigDecimal a1, BigInteger a2, RationalNumber a3) { };
-
- public void m4(float a1) { };
- public void m4(char a1) { };
-
- public void m5(Float a1) { };
- public void m5(Double a1) { };
- public void m5(Enum a1) { };
-
- public void m6(int a1) { };
- public void m6(File a1, Throwable a2) { };
- public void m6(File a1, Throwable a2, StringBuilder a3) { };
- public void m6(File a1, Throwable a2, Throwable a3) { };
- public void m6(double a1, int a2, File a3, File a4) { };
- public void m6(File a1, int a2, double a3, File a4) { };
- }
-
- public void testVarargsNums() {
- checkTypeFlags(VarArgsC.class, "m1",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER
- );
- checkTypeFlags(VarArgsC.class, "m2",
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m3",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER
- );
- checkTypeFlags(VarArgsC.class, "m3",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.INTEGER
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
- checkTypeFlags(VarArgsC.class, "m3",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.INTEGER
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.INTEGER
- | TypeFlags.BIG_DECIMAL | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m4",
- TypeFlags.INTEGER | TypeFlags.LONG
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m5",
- TypeFlags.LONG | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m6",
- TypeFlags.LONG | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- | TypeFlags.ACCEPTS_STRING
- );
-
- checkTypeFlags(VarArgsC.class, "m7",
- TypeFlags.INTEGER | TypeFlags.BYTE
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.FLOAT
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m8",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.FLOAT
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m9",
- TypeFlags.INTEGER | TypeFlags.BYTE
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m10",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER
- );
- checkTypeFlags(VarArgsC.class, "m10",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.LONG | TypeFlags.DOUBLE
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m11",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.SHORT | TypeFlags.ACCEPTS_NUMBER
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
- checkTypeFlags(VarArgsC.class, "m11",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.SHORT
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.LONG | TypeFlags.DOUBLE
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m12",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER
- );
- checkTypeFlags(VarArgsC.class, "m12",
- TypeFlags.INTEGER | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.SHORT
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.BYTE
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.LONG | TypeFlags.DOUBLE
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
-
- checkTypeFlags(VarArgsC.class, "m13",
- TypeFlags.CHARACTER,
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER);
- checkTypeFlags(VarArgsC.class, "m13",
- TypeFlags.CHARACTER,
- TypeFlags.DOUBLE | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.UNKNOWN_NUMERICAL_TYPE
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER,
- TypeFlags.DOUBLE | TypeFlags.LONG
- | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT | TypeFlags.ACCEPTS_NUMBER
- );
- }
-
- static public class VarArgsC {
- public void m1(int... va) { }
-
- public void m2(double a1, int... va) { }
-
- public void m3(int... va) { }
- public void m3(int a1, double... va) { }
- public void m3(int a1, double a2, BigDecimal... va) { }
-
- public void m4(int... va) { }
- public void m4(long... va) { }
-
- public void m5(Long... va) { }
- public void m5(long... va) { }
-
- public void m6(long... va) { }
- public void m6(String... va) { }
-
- public void m7(int a1, double... va) { }
- public void m7(byte a1, float... va) { }
-
- public void m8(int a1, double... va) { }
- public void m8(int a1, float... va) { }
-
- public void m9(int a1, double... va) { }
- public void m9(byte a1, double... va) { }
-
- public void m10(int a1, double a2, long... va) { }
- public void m10(int a1, double... va) { }
-
- public void m11(int a1, short a2, long... va) { }
- public void m11(int a1, double... va) { }
-
- public void m12(int a1, short a2, byte a3, long... va) { }
- public void m12(int a1, double... va) { }
-
- public void m13(char a1, double a2, RationalNumber a3, Long... va) { }
- public void m13(char a1, Double... va) { }
- }
-
- public void testAllZeros() {
- for (boolean reverse : new boolean[] { true, false }) {
- assertSame(OverloadedMethodsSubset.ALL_ZEROS_ARRAY, getTypeFlags(AllZeroC.class, "m1", reverse, 0));
- assertSame(OverloadedMethodsSubset.ALL_ZEROS_ARRAY, getTypeFlags(AllZeroC.class, "m2", reverse, 2));
- assertSame(OverloadedMethodsSubset.ALL_ZEROS_ARRAY, getTypeFlags(AllZeroC.class, "m3", reverse, 1));
- }
- }
-
- static public class AllZeroC {
- public void m1() {}
-
- public void m2(File a1, File a2) {}
-
- public void m3(File a1) {}
- public void m3(StringBuilder a1) {}
- }
-
- public void testAcceptanceNonOverloaded() {
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber1", TypeFlags.ACCEPTS_NUMBER | TypeFlags.UNKNOWN_NUMERICAL_TYPE);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber2", TypeFlags.ACCEPTS_NUMBER | TypeFlags.BYTE);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber3", TypeFlags.ACCEPTS_NUMBER | TypeFlags.BYTE);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber4", TypeFlags.ACCEPTS_NUMBER | TypeFlags.SHORT);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber5", TypeFlags.ACCEPTS_NUMBER | TypeFlags.SHORT);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber6", TypeFlags.ACCEPTS_NUMBER | TypeFlags.INTEGER);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber7", TypeFlags.ACCEPTS_NUMBER | TypeFlags.INTEGER);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber8", TypeFlags.ACCEPTS_NUMBER | TypeFlags.LONG);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber9", TypeFlags.ACCEPTS_NUMBER | TypeFlags.LONG);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber10", TypeFlags.ACCEPTS_NUMBER | TypeFlags.FLOAT);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber11", TypeFlags.ACCEPTS_NUMBER | TypeFlags.FLOAT);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber12", TypeFlags.ACCEPTS_NUMBER | TypeFlags.DOUBLE);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber13", TypeFlags.ACCEPTS_NUMBER | TypeFlags.DOUBLE);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber14", TypeFlags.ACCEPTS_NUMBER | TypeFlags.BIG_INTEGER);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber15", TypeFlags.ACCEPTS_NUMBER | TypeFlags.BIG_DECIMAL);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mNumber16", TypeFlags.ACCEPTS_NUMBER | TypeFlags.UNKNOWN_NUMERICAL_TYPE);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mDate", TypeFlags.ACCEPTS_DATE);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mSQLDate1", 0);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mSQLDate2", 0);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mString", TypeFlags.ACCEPTS_STRING);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mCharSequence", TypeFlags.ACCEPTS_STRING);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mStringBuilder", 0);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mBool", TypeFlags.ACCEPTS_BOOLEAN);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mBoolean", TypeFlags.ACCEPTS_BOOLEAN);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mMap1", TypeFlags.ACCEPTS_MAP);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mMap2", 0);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mList1", TypeFlags.ACCEPTS_LIST);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mList2", 0);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mSet1", TypeFlags.ACCEPTS_SET);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mSet2", 0);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mCollection", TypeFlags.ACCEPTS_SET | TypeFlags.ACCEPTS_LIST);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mChar1", TypeFlags.CHARACTER);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mChar2", TypeFlags.CHARACTER);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mArray1", TypeFlags.ACCEPTS_ARRAY);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mArray2", TypeFlags.ACCEPTS_ARRAY);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mArray3", TypeFlags.ACCEPTS_ARRAY);
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mArray4", TypeFlags.ACCEPTS_ARRAY);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mObject", TypeFlags.ACCEPTS_ANY_OBJECT);
- assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_NUMBER) != 0);
- assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_STRING) != 0);
- assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_BOOLEAN) != 0);
- assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_MAP) != 0);
- assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_LIST) != 0);
- assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_SET) != 0);
- assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.ACCEPTS_ARRAY) != 0);
- assertTrue((TypeFlags.ACCEPTS_ANY_OBJECT & TypeFlags.CHARACTER) == 0); // deliberatly 0
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mMapDate", 0);
-
- checkTypeFlags(AcceptanceNonoverloadedC.class, "mDateBooleanList",
- TypeFlags.ACCEPTS_DATE, TypeFlags.ACCEPTS_BOOLEAN, TypeFlags.ACCEPTS_LIST);
- }
-
- static public class AcceptanceNonoverloadedC {
- public void mNumber1(Number a1) {}
- public void mNumber2(Byte a1) {}
- public void mNumber3(byte a1) {}
- public void mNumber4(Short a1) {}
- public void mNumber5(short a1) {}
- public void mNumber6(Integer a1) {}
- public void mNumber7(int a1) {}
- public void mNumber8(Long a1) {}
- public void mNumber9(long a1) {}
- public void mNumber10(Float a1) {}
- public void mNumber11(float a1) {}
- public void mNumber12(Double a1) {}
- public void mNumber13(double a1) {}
- public void mNumber14(BigInteger a1) {}
- public void mNumber15(BigDecimal a1) {}
- public void mNumber16(RationalNumber a1) {}
-
- public void mDate(Date a1) {}
- public void mSQLDate1(java.sql.Date a1) {}
- public void mSQLDate2(java.sql.Timestamp a1) {}
-
- public void mString(String a1) {}
- public void mCharSequence(CharSequence a1) {}
- public void mStringBuilder(StringBuilder a1) {}
-
- public void mBool(boolean a1) {}
- public void mBoolean(Boolean a1) {}
-
- public void mMap1(Map a1) {}
- public void mMap2(LinkedHashMap a1) {}
-
- public void mList1(List a1) {}
- public void mList2(ArrayList a1) {}
-
- public void mSet1(Set a1) {}
- public void mSet2(HashSet a1) {}
-
- public void mCollection(Collection a1) {}
-
- public void mMapDate(MapDate a1) {}
-
- public void mChar1(Character a1) {}
- public void mChar2(char a1) {}
-
- public void mArray1(Object[] a1) {}
- public void mArray2(int[] a1) {}
- public void mArray3(Integer[] a1) {}
- public void mArray4(Void[] a1) {}
-
- public void mObject(Object a1) {}
-
- public void mDateBooleanList(Date a1, boolean a2, List a3) {}
- }
-
- public void testAcceptanceOverloaded() {
- checkTypeFlags(AcceptanceOverloadedC.class, "mLongDateList",
- TypeFlags.ACCEPTS_NUMBER | TypeFlags.LONG | TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT
- | TypeFlags.ACCEPTS_DATE | TypeFlags.ACCEPTS_LIST);
- checkTypeFlags(AcceptanceOverloadedC.class, "mBoolean", TypeFlags.ACCEPTS_BOOLEAN);
- checkTypeFlags(AcceptanceOverloadedC.class, "mStringChar",
- TypeFlags.ACCEPTS_STRING | TypeFlags.CHARACTER);
- checkTypeFlags(AcceptanceOverloadedC.class, "mStringFile", TypeFlags.ACCEPTS_STRING);
- checkTypeFlags(AcceptanceOverloadedC.class, "mMapObject", TypeFlags.ACCEPTS_ANY_OBJECT);
- checkTypeFlags(AcceptanceOverloadedC.class, "mSetMap", TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_SET);
- checkTypeFlags(AcceptanceOverloadedC.class, "mCollectionMap",
- TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_SET | TypeFlags.ACCEPTS_LIST);
- checkTypeFlags(AcceptanceOverloadedC.class, "mArray", TypeFlags.ACCEPTS_ARRAY);
- checkTypeFlags(AcceptanceOverloadedC.class, "mArrayList", TypeFlags.ACCEPTS_ARRAY | TypeFlags.ACCEPTS_LIST);
-
- checkTypeFlags(AcceptanceOverloadedC.class, "mStringCollectionThenBooleanThenMapList",
- TypeFlags.ACCEPTS_STRING | TypeFlags.ACCEPTS_LIST | TypeFlags.ACCEPTS_SET,
- TypeFlags.ACCEPTS_BOOLEAN,
- TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_LIST);
- }
-
- static public class AcceptanceOverloadedC {
- public void mLongDateList(long a1) {}
- public void mLongDateList(Date a1) {}
- public void mLongDateList(List a1) {}
-
- public void mBoolean(boolean a1) {}
- public void mBoolean(Boolean a1) {}
-
- public void mDate(Date a1) {}
- public void mDate(java.sql.Date a1) {}
- public void mDate(java.sql.Timestamp a1) {}
-
- public void mStringChar(String a1) {}
- public void mStringChar(char a1) {}
- public void mStringChar(Character a1) {}
-
- public void mStringFile(String a1) {}
- public void mStringFile(File a1) {}
-
- public void mMapObject(Map a1) {}
- public void mMapObject(Object a1) {}
-
- public void mSetMap(Set a1) {}
- public void mSetMap(Map a1) {}
-
- public void mCollectionMap(Collection a1) {}
- public void mCollectionMap(Map a1) {}
-
- public void mArray(Object[] a1) {}
- public void mArray(int[] a1) {}
- public void mArray(Integer[] a1) {}
- public void mArray(Void[] a1) {}
-
- public void mArrayList(String[] a1) {}
- public void mArrayList(List a1) {}
-
- public void mStringCollectionThenBooleanThenMapList(String a1, boolean a2, Map a3) {}
- public void mStringCollectionThenBooleanThenMapList(Collection a1, boolean a2, Map a3) {}
- public void mStringCollectionThenBooleanThenMapList(String a1, boolean a2, List a3) {}
- }
-
- public void testAcceptanceVarargsC() {
- checkTypeFlags(AcceptanceVarArgsC.class, "m1",
- TypeFlags.ACCEPTS_LIST | TypeFlags.ACCEPTS_STRING);
-
- checkTypeFlags(AcceptanceVarArgsC.class, "m2",
- TypeFlags.ACCEPTS_MAP,
- TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_STRING | TypeFlags.ACCEPTS_BOOLEAN,
- TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_STRING);
- checkTypeFlags(AcceptanceVarArgsC.class, "m2",
- TypeFlags.ACCEPTS_MAP,
- TypeFlags.ACCEPTS_MAP | TypeFlags.ACCEPTS_STRING | TypeFlags.ACCEPTS_BOOLEAN);
- checkTypeFlags(AcceptanceVarArgsC.class, "m2",
- TypeFlags.ACCEPTS_MAP);
-
- checkTypeFlags(AcceptanceVarArgsC.class, "m3",
- TypeFlags.ACCEPTS_BOOLEAN);
- checkTypeFlags(AcceptanceVarArgsC.class, "m3",
- TypeFlags.ACCEPTS_BOOLEAN | TypeFlags.ACCEPTS_STRING,
- TypeFlags.ACCEPTS_BOOLEAN | TypeFlags.ACCEPTS_MAP,
- TypeFlags.ACCEPTS_BOOLEAN | TypeFlags.CHARACTER,
- TypeFlags.ACCEPTS_BOOLEAN);
- }
-
- static public class AcceptanceVarArgsC {
- public void m1(List... a1) {}
- public void m1(String... a1) {}
-
- public void m2(Map a1, String... a2) {}
- public void m2(Map a1, boolean a2, String... a3) {}
- public void m2(Map... a1) {}
-
- public void m3(String a1, Map a2, char a3, Boolean... a4) {}
- public void m3(boolean... a1) {}
- }
-
- static public class MapDate extends Date implements Map {
-
- @Override
- public int size() {
- return 0;
- }
-
- @Override
- public boolean isEmpty() {
- return false;
- }
-
- @Override
- public boolean containsKey(Object key) {
- return false;
- }
-
- @Override
- public boolean containsValue(Object value) {
- return false;
- }
-
- @Override
- public Object get(Object key) {
- return null;
- }
-
- @Override
- public Object put(Object key, Object value) {
- return null;
- }
-
- @Override
- public Object remove(Object key) {
- return null;
- }
-
- @Override
- public void putAll(Map m) {
- }
-
- @Override
- public void clear() {
- }
-
- @Override
- public Set keySet() {
- return null;
- }
-
- @Override
- public Collection values() {
- return null;
- }
-
- @Override
- public Set entrySet() {
- return null;
- }
-
- }
-
- private OverloadedMethodsSubset newOverloadedMethodsSubset(Class cl, String methodName, final boolean desc) {
- final Method[] ms = cl.getMethods();
-
- final List<Method> filteredMethods = new ArrayList();
- for (Method m : ms) {
- if (m.getName().equals(methodName)) {
- filteredMethods.add(m);
- }
- }
- // As the order in which getMethods() returns the methods is undefined, we sort them for test predictability:
- Collections.sort(filteredMethods, new Comparator<Method>() {
- @Override
- public int compare(Method o1, Method o2) {
- int res = o1.toString().compareTo(o2.toString());
- return desc ? -res : res;
- }
- });
-
- final OverloadedMethodsSubset oms = cl.getName().indexOf("VarArgs") == -1
- ? new OverloadedFixArgsMethods() : new OverloadedVarArgsMethods();
- for (Method m : filteredMethods) {
- oms.addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(m, m.getParameterTypes()));
- }
- return oms;
- }
-
- private void checkTypeFlags(Class cl, String methodName, int... expectedTypeFlags) {
- checkTypeFlags(cl, methodName, false, expectedTypeFlags);
- checkTypeFlags(cl, methodName, true, expectedTypeFlags);
- }
-
- private void checkTypeFlags(Class cl, String methodName, boolean revMetOrder, int... expectedTypeFlags) {
- int[] actualParamTypes = getTypeFlags(cl, methodName, revMetOrder, expectedTypeFlags.length);
- assertNotNull("Method " + methodName + "(#" + expectedTypeFlags.length + ") doesn't exist", actualParamTypes);
- if (actualParamTypes != OverloadedMethodsSubset.ALL_ZEROS_ARRAY) {
- assertEquals(expectedTypeFlags.length, actualParamTypes.length);
- for (int i = 0; i < expectedTypeFlags.length; i++) {
- assertEquals(expectedTypeFlags[i], actualParamTypes[i]);
- }
- } else {
- for (int expectedTypeFlag : expectedTypeFlags) {
- assertEquals(expectedTypeFlag, 0);
- }
- }
- }
-
- private int[] getTypeFlags(Class cl, String methodName, boolean revMetOrder, int paramCnt) {
- OverloadedMethodsSubset oms = newOverloadedMethodsSubset(cl, methodName, revMetOrder);
- int[] actualParamTypes = oms.getTypeFlags(paramCnt);
- return actualParamTypes;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java b/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java
index d190704..26205d9 100644
--- a/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java
+++ b/src/test/java/org/apache/freemarker/servlet/jsp/RealServletContainertTest.java
@@ -41,9 +41,8 @@ import org.apache.freemarker.core.Configuration;
import org.apache.freemarker.core.TemplateExceptionHandler;
import org.apache.freemarker.core.model.ObjectWrapper;
import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
import org.apache.freemarker.core.model.impl.SimpleObjectWrapper;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder;
import org.apache.freemarker.core.templateresolver.TemplateLoader;
import org.apache.freemarker.core.templateresolver.impl.ClassTemplateLoader;
import org.apache.freemarker.servlet.FreemarkerServlet;
@@ -411,7 +410,7 @@ public class RealServletContainertTest extends WebAppTestCase {
Configuration cfg = new Configuration(Configuration.VERSION_3_0_0);
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(true);
- BeansWrapperBuilder bwb = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
+ DefaultObjectWrapperBuilder bwb = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
bwb.setUseModelCache(true);
cfg.setObjectWrapper(bwb.build());
cfg.setTemplateLoader(new WebAppTemplateLoader(getServletContext()));
@@ -428,9 +427,9 @@ public class RealServletContainertTest extends WebAppTestCase {
{
ObjectWrapper ow = cfg.getObjectWrapper();
- assertSame(BeansWrapper.class, ow.getClass());
- assertTrue(((BeansWrapper) ow).getUseModelCache());
- assertEquals(Configuration.VERSION_3_0_0, ((BeansWrapper) ow).getIncompatibleImprovements());
+ assertSame(DefaultObjectWrapper.class, ow.getClass());
+ assertTrue(((DefaultObjectWrapper) ow).getUseModelCache());
+ assertEquals(Configuration.VERSION_3_0_0, ((DefaultObjectWrapper) ow).getIncompatibleImprovements());
}
{
@@ -447,13 +446,13 @@ public class RealServletContainertTest extends WebAppTestCase {
@Override
protected void doAssertions(Configuration cfg) throws Exception {
ObjectWrapper ow = cfg.getObjectWrapper();
- assertSame(BeansWrapper.class, ow.getClass());
- assertTrue(((BeansWrapper) ow).getUseModelCache());
+ assertSame(DefaultObjectWrapper.class, ow.getClass());
+ assertTrue(((DefaultObjectWrapper) ow).getUseModelCache());
}
@Override
protected ObjectWrapper createDefaultObjectWrapper() {
- BeansWrapperBuilder bwb = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
+ DefaultObjectWrapperBuilder bwb = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
bwb.setUseModelCache(true);
assertEquals(Configuration.VERSION_3_0_0, bwb.getIncompatibleImprovements());
return bwb.build();
@@ -475,7 +474,7 @@ public class RealServletContainertTest extends WebAppTestCase {
protected void doAssertions(Configuration cfg) throws Exception {
ObjectWrapper ow = cfg.getObjectWrapper();
assertSame(SimpleObjectWrapper.class, ow.getClass());
- assertEquals(Configuration.VERSION_3_0_0, ((BeansWrapper) ow).getIncompatibleImprovements());
+ assertEquals(Configuration.VERSION_3_0_0, ((DefaultObjectWrapper) ow).getIncompatibleImprovements());
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java b/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
index 396e8aa..e2468df 100644
--- a/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
+++ b/src/test/java/org/apache/freemarker/servlet/jsp/TLDParsingTest.java
@@ -35,9 +35,9 @@ import javax.xml.parsers.SAXParserFactory;
import org.apache.freemarker.core.Configuration;
import org.apache.freemarker.core.model.TemplateMethodModelEx;
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.beans.BeansWrapper;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,11 +48,11 @@ import org.xml.sax.XMLReader;
@RunWith(JUnit4.class)
public class TLDParsingTest {
- private BeansWrapper wrapper;
+ private DefaultObjectWrapper wrapper;
@Before
public void before() throws Exception {
- wrapper = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ wrapper = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
}
@Test
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java b/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java
index 4fab03b..cce84a4 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/TemplateTestCase.java
@@ -54,17 +54,15 @@ import org.apache.freemarker.core.model.TemplateMethodModel;
import org.apache.freemarker.core.model.TemplateNodeModel;
import org.apache.freemarker.core.model.TemplateScalarModel;
import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.BooleanModel;
import org.apache.freemarker.core.model.impl.DefaultNonListCollectionAdapter;
import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
+import org.apache.freemarker.core.model.impl.ResourceBundleModel;
import org.apache.freemarker.core.model.impl.SimpleCollection;
import org.apache.freemarker.core.model.impl.SimpleDate;
import org.apache.freemarker.core.model.impl.SimpleNumber;
import org.apache.freemarker.core.model.impl._StaticObjectWrappers;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder;
-import org.apache.freemarker.core.model.impl.beans.BooleanModel;
-import org.apache.freemarker.core.model.impl.beans.Java7MembersOnlyBeansWrapper;
-import org.apache.freemarker.core.model.impl.beans.ResourceBundleModel;
import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader;
import org.apache.freemarker.core.util._NullArgumentException;
import org.apache.freemarker.core.util._NullWriter;
@@ -183,7 +181,7 @@ public class TemplateTestCase extends FileTestCase {
conf.setTemplateLoader(new CopyrightCommentRemoverTemplateLoader(
new FileTemplateLoader(new File(getTestClassDirectory(), "templates"))));
- BeansWrapper beansWrapper = new BeansWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ DefaultObjectWrapper dow = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
dataModel.put(ASSERT_VAR_NAME, AssertDirective.INSTANCE);
dataModel.put(ASSERT_EQUALS_VAR_NAME, AssertEqualsDirective.INSTANCE);
@@ -201,46 +199,7 @@ public class TemplateTestCase extends FileTestCase {
dataModel.put("list", ImmutableList.of(1, 2, 3));
dataModel.put("set", ImmutableSet.of("a", "b", "c"));
dataModel.put("s", "test");
- } else if (simpleTestName.equals("bean-maps")) {
- BeansWrapper w1 = new Java7MembersOnlyBeansWrapper();
- BeansWrapper w2 = new Java7MembersOnlyBeansWrapper();
- BeansWrapper w3 = new Java7MembersOnlyBeansWrapper();
- BeansWrapper w4 = new Java7MembersOnlyBeansWrapper();
- BeansWrapper w5 = new Java7MembersOnlyBeansWrapper();
- BeansWrapper w6 = new Java7MembersOnlyBeansWrapper();
- BeansWrapper w7 = new Java7MembersOnlyBeansWrapper();
-
- w1.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
- w2.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
- w3.setExposureLevel(BeansWrapper.EXPOSE_NOTHING);
- w4.setExposureLevel(BeansWrapper.EXPOSE_NOTHING);
- w5.setExposureLevel(BeansWrapper.EXPOSE_ALL);
- w6.setExposureLevel(BeansWrapper.EXPOSE_ALL);
-
- w1.setMethodsShadowItems(true);
- w2.setMethodsShadowItems(false);
- w3.setMethodsShadowItems(true);
- w4.setMethodsShadowItems(false);
- w5.setMethodsShadowItems(true);
- w6.setMethodsShadowItems(false);
-
- w7.setSimpleMapWrapper(true);
-
- Object test = getTestMapBean();
-
- dataModel.put("m1", w1.wrap(test));
- dataModel.put("m2", w2.wrap(test));
- dataModel.put("m3", w3.wrap(test));
- dataModel.put("m4", w4.wrap(test));
- dataModel.put("m5", w5.wrap(test));
- dataModel.put("m6", w6.wrap(test));
- dataModel.put("m7", w7.wrap(test));
-
- dataModel.put("s1", w1.wrap("hello"));
- dataModel.put("s2", w1.wrap("world"));
- dataModel.put("s3", w5.wrap("hello"));
- dataModel.put("s4", w5.wrap("world"));
- } else if (simpleTestName.equals("beans")) {
+ } else if (simpleTestName.equals("default-object-wrapper")) {
dataModel.put("array", new String[] { "array-0", "array-1"});
dataModel.put("list", Arrays.asList("list-0", "list-1", "list-2"));
Map<Object, Object> tmap = new HashMap<>();
@@ -253,10 +212,10 @@ public class TemplateTestCase extends FileTestCase {
dataModel.put("resourceBundle",
new ResourceBundleModel(ResourceBundle.getBundle(
"org.apache.freemarker.test.templatesuite.models.BeansTestResources"),
- _StaticObjectWrappers.BEANS_WRAPPER));
+ _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER));
dataModel.put("date", new GregorianCalendar(1974, 10, 14).getTime());
- dataModel.put("statics", beansWrapper.getStaticModels());
- dataModel.put("enums", beansWrapper.getEnumModels());
+ dataModel.put("statics", dow.getStaticModels());
+ dataModel.put("enums", dow.getEnumModels());
} else if (simpleTestName.equals("boolean")) {
dataModel.put( "boolean1", TemplateBooleanModel.FALSE);
dataModel.put( "boolean2", TemplateBooleanModel.TRUE);
@@ -381,7 +340,7 @@ public class TemplateTestCase extends FileTestCase {
abcSet.add("b");
abcSet.add("c");
dataModel.put("abcSet", abcSet);
- dataModel.put("abcSetNonSeq", DefaultNonListCollectionAdapter.adapt(abcSet, beansWrapper));
+ dataModel.put("abcSetNonSeq", DefaultNonListCollectionAdapter.adapt(abcSet, dow));
List<String> listWithNull = new ArrayList<>();
listWithNull.add("a");
@@ -408,7 +367,7 @@ public class TemplateTestCase extends FileTestCase {
} else if (simpleTestName.equals("varargs")) {
dataModel.put("m", new VarArgTestModel());
} else if (simpleTestName.startsWith("boolean-formatting")) {
- dataModel.put("beansBoolean", new BooleanModel(Boolean.TRUE, (BeansWrapper) conf.getObjectWrapper()));
+ dataModel.put("beansBoolean", new BooleanModel(Boolean.TRUE, (DefaultObjectWrapper) conf.getObjectWrapper()));
dataModel.put("booleanAndString", new BooleanAndStringTemplateModel());
dataModel.put("booleanVsStringMethods", new BooleanVsStringMethods());
} else if (simpleTestName.startsWith("number-math-builtins")) {
@@ -436,8 +395,6 @@ public class TemplateTestCase extends FileTestCase {
dataModel.put("bdp", BigDecimal.valueOf(0.05));
} else if (simpleTestName.startsWith("overloaded-methods")) {
dataModel.put("obj", new OverloadedMethods2());
- final boolean dow = conf.getObjectWrapper() instanceof DefaultObjectWrapper;
- dataModel.put("dow", dow);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/test/templatesuite/models/Listables.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/models/Listables.java b/src/test/java/org/apache/freemarker/test/templatesuite/models/Listables.java
index da5d62f..3d98f4b 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/models/Listables.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/models/Listables.java
@@ -41,7 +41,6 @@ import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
import org.apache.freemarker.core.model.impl.SimpleCollection;
import org.apache.freemarker.core.model.impl.SimpleHash;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapperBuilder;
import com.google.common.collect.ImmutableMap;
@@ -139,10 +138,6 @@ public class Listables {
DefaultObjectWrapper ow = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
maps.add(new SimpleHash(map, ow));
maps.add((DefaultMapAdapter) ow.wrap(map));
-
- BeansWrapperBuilder bwb = new BeansWrapperBuilder(Configuration.VERSION_3_0_0);
- bwb.setSimpleMapWrapper(true);
- maps.add((TemplateHashModelEx2) bwb.build().wrap(map));
return maps;
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/test/templatesuite/models/OverloadedMethods2.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/templatesuite/models/OverloadedMethods2.java b/src/test/java/org/apache/freemarker/test/templatesuite/models/OverloadedMethods2.java
index c12406c..78d3462 100644
--- a/src/test/java/org/apache/freemarker/test/templatesuite/models/OverloadedMethods2.java
+++ b/src/test/java/org/apache/freemarker/test/templatesuite/models/OverloadedMethods2.java
@@ -37,7 +37,7 @@ import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelException;
import org.apache.freemarker.core.model.TemplateNumberModel;
import org.apache.freemarker.core.model.WrapperTemplateModel;
-import org.apache.freemarker.core.model.impl.beans.RationalNumber;
+import org.apache.freemarker.core.model.impl.RationalNumber;
import org.apache.freemarker.core.util._StringUtil;
import org.apache.freemarker.test.util.TestUtil;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/test/util/SimpleMapAndCollectionObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/test/util/SimpleMapAndCollectionObjectWrapper.java b/src/test/java/org/apache/freemarker/test/util/SimpleMapAndCollectionObjectWrapper.java
index 1c787ce..1c61994 100644
--- a/src/test/java/org/apache/freemarker/test/util/SimpleMapAndCollectionObjectWrapper.java
+++ b/src/test/java/org/apache/freemarker/test/util/SimpleMapAndCollectionObjectWrapper.java
@@ -18,6 +18,7 @@
*/
package org.apache.freemarker.test.util;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
@@ -44,7 +45,7 @@ public class SimpleMapAndCollectionObjectWrapper extends DefaultObjectWrapper {
return super.wrap(null);
}
if (obj.getClass().isArray()) {
- obj = convertArray(obj);
+ obj = Arrays.asList((Object[]) obj);
}
if (obj instanceof Collection) {
return new SimpleSequence((Collection<?>) obj, this);
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes-modernModels.txt
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes-modernModels.txt b/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes-modernModels.txt
index 01c8f88..c151f58 100644
--- a/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes-modernModels.txt
+++ b/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes-modernModels.txt
@@ -60,8 +60,8 @@ String: s [java.lang.String]
Number: 1 [java.math.BigDecimal]
Boolean: true [java.lang.Boolean]
Date-time: 2014-12-20T16:19:00Z [java.util.Date]
-Sequence: [1, 2, 3] [org.apache.freemarker.core.model.impl.beans.SequenceAdapter]
-Hash: {a=1, b=2, c=3} [org.apache.freemarker.core.model.impl.beans.HashAdapter]
+Sequence: [1, 2, 3] [org.apache.freemarker.core.model.impl.SequenceAdapter]
+Hash: {a=1, b=2, c=3} [org.apache.freemarker.core.model.impl.HashAdapter]
Values created in Java:
LinkedList: [1, 2, 3] [java.util.LinkedList]
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes.txt
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes.txt b/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes.txt
index 01c8f88..c151f58 100644
--- a/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes.txt
+++ b/src/test/resources/org/apache/freemarker/servlet/jsp/webapps/basic/WEB-INF/expected/attributes.txt
@@ -60,8 +60,8 @@ String: s [java.lang.String]
Number: 1 [java.math.BigDecimal]
Boolean: true [java.lang.Boolean]
Date-time: 2014-12-20T16:19:00Z [java.util.Date]
-Sequence: [1, 2, 3] [org.apache.freemarker.core.model.impl.beans.SequenceAdapter]
-Hash: {a=1, b=2, c=3} [org.apache.freemarker.core.model.impl.beans.HashAdapter]
+Sequence: [1, 2, 3] [org.apache.freemarker.core.model.impl.SequenceAdapter]
+Hash: {a=1, b=2, c=3} [org.apache.freemarker.core.model.impl.HashAdapter]
Values created in Java:
LinkedList: [1, 2, 3] [java.util.LinkedList]
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/resources/org/apache/freemarker/test/templatesuite/expected/bean-maps.txt
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/freemarker/test/templatesuite/expected/bean-maps.txt b/src/test/resources/org/apache/freemarker/test/templatesuite/expected/bean-maps.txt
deleted file mode 100644
index e450843..0000000
--- a/src/test/resources/org/apache/freemarker/test/templatesuite/expected/bean-maps.txt
+++ /dev/null
@@ -1,115 +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.
- */
-properties only, shadow (6):
- age : 27
- class : class org.apache.freemarker.test.templatesuite.TemplateTestCase$TestMapBean
- empty : false
- location : San Francisco
- luckyNumber : 7
- name : Christopher
-
-properties only (6):
- age : 27
- class : class org.apache.freemarker.test.templatesuite.TemplateTestCase$TestMapBean
- empty : false
- location : San Francisco
- luckyNumber : 7
- name : Chris
-
-nothing, shadow (3):
- age : 27
- location : San Francisco
- name : Chris
-
-nothing (3):
- age : 27
- location : San Francisco
- name : Chris
-
-all, shadow (28):
- age : 27
- class : class org.apache.freemarker.test.templatesuite.TemplateTestCase$TestMapBean
- clear : UNKNOWN
- clone : UNKNOWN
- containsKey : UNKNOWN
- containsValue : UNKNOWN
- empty : false
- entrySet : UNKNOWN
- equals : UNKNOWN
- get : UNKNOWN
- getClass : UNKNOWN
- getLuckyNumber : UNKNOWN
- getName : UNKNOWN
- hashCode : UNKNOWN
- isEmpty : UNKNOWN
- keySet : UNKNOWN
- location : San Francisco
- luckyNumber : 7
- name : Christopher
- notify : UNKNOWN
- notifyAll : UNKNOWN
- put : UNKNOWN
- putAll : UNKNOWN
- remove : UNKNOWN
- size : UNKNOWN
- toString : UNKNOWN
- values : UNKNOWN
- wait : UNKNOWN
-
-all (28):
- age : 27
- class : class org.apache.freemarker.test.templatesuite.TemplateTestCase$TestMapBean
- clear : UNKNOWN
- clone : UNKNOWN
- containsKey : UNKNOWN
- containsValue : UNKNOWN
- empty : false
- entrySet : UNKNOWN
- equals : UNKNOWN
- get : UNKNOWN
- getClass : UNKNOWN
- getLuckyNumber : UNKNOWN
- getName : UNKNOWN
- hashCode : UNKNOWN
- isEmpty : UNKNOWN
- keySet : UNKNOWN
- location : San Francisco
- luckyNumber : 7
- name : Chris
- notify : UNKNOWN
- notifyAll : UNKNOWN
- put : UNKNOWN
- putAll : UNKNOWN
- remove : UNKNOWN
- size : UNKNOWN
- toString : UNKNOWN
- values : UNKNOWN
- wait : UNKNOWN
-
-simple map mode (3):
- age : 27
- location : San Francisco
- name : Chris
-
-
-String concatenation:
- helloworld
- helloworld
- hellohello
- worldworld
[07/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperSingletonsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperSingletonsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperSingletonsTest.java
new file mode 100644
index 0000000..f98f0f8
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperSingletonsTest.java
@@ -0,0 +1,689 @@
+/*
+ * 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.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.lang.ref.Reference;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core.model.TemplateDateModel;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.test.util.TestUtil;
+
+import junit.framework.TestCase;
+
+public class DefaultObjectWrapperSingletonsTest extends TestCase {
+
+ public DefaultObjectWrapperSingletonsTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ DefaultObjectWrapperBuilder.clearInstanceCache();
+ }
+
+ public void testBuilderEqualsAndHash() throws Exception {
+ assertEquals(Configuration.VERSION_3_0_0, new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).getIncompatibleImprovements());
+ try {
+ new DefaultObjectWrapperBuilder(TestUtil.getClosestFutureVersion());
+ fail("Maybe you need to update this test for the new FreeMarker version");
+ } catch (IllegalArgumentException e) {
+ assertThat(e.getMessage(), containsString("upgrade"));
+ }
+
+ DefaultObjectWrapperBuilder builder1;
+ DefaultObjectWrapperBuilder builder2;
+
+ builder1 = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ builder2 = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ assertEquals(builder1, builder2);
+
+ builder1.setExposeFields(true);
+ assertNotEquals(builder1, builder2);
+ assertFalse(builder1.hashCode() == builder2.hashCode());
+ builder2.setExposeFields(true);
+ assertEquals(builder1, builder2);
+ assertTrue(builder1.hashCode() == builder2.hashCode());
+
+ builder1.setExposureLevel(0);
+ assertNotEquals(builder1, builder2);
+ assertFalse(builder1.hashCode() == builder2.hashCode());
+ builder2.setExposureLevel(0);
+ assertEquals(builder1, builder2);
+ assertTrue(builder1.hashCode() == builder2.hashCode());
+
+ builder1.setExposureLevel(1);
+ assertNotEquals(builder1, builder2);
+ assertFalse(builder1.hashCode() == builder2.hashCode());
+ builder2.setExposureLevel(1);
+ assertEquals(builder1, builder2);
+ assertTrue(builder1.hashCode() == builder2.hashCode());
+
+ builder1.setDefaultDateType(TemplateDateModel.DATE);
+ assertNotEquals(builder1, builder2);
+ builder2.setDefaultDateType(TemplateDateModel.DATE);
+ assertEquals(builder1, builder2);
+ assertTrue(builder1.hashCode() == builder2.hashCode());
+
+ builder1.setStrict(true);
+ assertNotEquals(builder1, builder2);
+ assertFalse(builder1.hashCode() == builder2.hashCode());
+ builder2.setStrict(true);
+ assertEquals(builder1, builder2);
+ assertTrue(builder1.hashCode() == builder2.hashCode());
+
+ builder1.setUseModelCache(true);
+ assertNotEquals(builder1, builder2);
+ assertFalse(builder1.hashCode() == builder2.hashCode());
+ builder2.setUseModelCache(true);
+ assertEquals(builder1, builder2);
+ assertTrue(builder1.hashCode() == builder2.hashCode());
+
+ AlphabeticalMethodSorter ms = new AlphabeticalMethodSorter(true);
+ builder1.setMethodSorter(ms);
+ assertNotEquals(builder1, builder2);
+ builder2.setMethodSorter(ms);
+ assertEquals(builder1, builder2);
+ assertTrue(builder1.hashCode() == builder2.hashCode());
+
+ MethodAppearanceFineTuner maft = new MethodAppearanceFineTuner() {
+ @Override
+ public void process(DecisionInput in, Decision out) { }
+ };
+ builder1.setMethodAppearanceFineTuner(maft);
+ assertNotEquals(builder1, builder2);
+ builder2.setMethodAppearanceFineTuner(maft);
+ assertEquals(builder1, builder2);
+ assertTrue(builder1.hashCode() == builder2.hashCode());
+ }
+
+ public void testDefaultObjectWrapperBuilderProducts() throws Exception {
+ List<DefaultObjectWrapper> hardReferences = new LinkedList<>();
+
+ assertEquals(0, getDefaultObjectWrapperInstanceCacheSize());
+
+ {
+ DefaultObjectWrapper ow = getDefaultObjectWrapperWithSetting(Configuration.VERSION_3_0_0, true);
+ assertEquals(1, getDefaultObjectWrapperInstanceCacheSize());
+ assertSame(ow.getClass(), DefaultObjectWrapper.class);
+ assertEquals(Configuration.VERSION_3_0_0, ow.getIncompatibleImprovements());
+ assertTrue(ow.isWriteProtected());
+ assertFalse(ow.isStrict());
+ assertTrue(ow.getUseModelCache());
+ assertEquals(TemplateDateModel.UNKNOWN, ow.getDefaultDateType());
+ assertSame(ow, ow.getOuterIdentity());
+ assertTrue(ow.isClassIntrospectionCacheRestricted());
+ assertNull(ow.getMethodAppearanceFineTuner());
+ assertNull(ow.getMethodSorter());
+
+ try {
+ ow.setExposeFields(true); // can't modify the settings of a (potential) singleton
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e.getMessage(), containsString("modify"));
+ }
+
+ assertSame(ow, getDefaultObjectWrapperWithSetting(Configuration.VERSION_3_0_0, true));
+ assertEquals(1, getDefaultObjectWrapperInstanceCacheSize());
+
+ hardReferences.add(ow);
+ }
+
+ {
+ DefaultObjectWrapper ow = getDefaultObjectWrapperWithSetting(Configuration.VERSION_3_0_0, false);
+ assertEquals(2, getDefaultObjectWrapperInstanceCacheSize());
+ assertSame(ow.getClass(), DefaultObjectWrapper.class);
+ assertEquals(Configuration.VERSION_3_0_0, ow.getIncompatibleImprovements());
+ assertTrue(ow.isWriteProtected());
+ assertFalse(ow.getUseModelCache());
+
+ assertSame(ow, getDefaultObjectWrapperWithSetting(Configuration.VERSION_3_0_0, false));
+
+ hardReferences.add(ow);
+ }
+
+ {
+ DefaultObjectWrapperBuilder factory = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ factory.setExposureLevel(DefaultObjectWrapper.EXPOSE_PROPERTIES_ONLY);
+ DefaultObjectWrapper ow1 = factory.build();
+ DefaultObjectWrapper ow2 = factory.build();
+ assertEquals(3, getDefaultObjectWrapperInstanceCacheSize());
+ assertSame(ow1, ow2);
+
+ assertSame(ow1.getClass(), DefaultObjectWrapper.class);
+ assertEquals(Configuration.VERSION_3_0_0, ow1.getIncompatibleImprovements());
+ assertTrue(ow1.isWriteProtected());
+ assertEquals(DefaultObjectWrapper.EXPOSE_PROPERTIES_ONLY, ow1.getExposureLevel());
+ assertFalse(ow1.isStrict());
+ assertEquals(TemplateDateModel.UNKNOWN, ow1.getDefaultDateType());
+ assertSame(ow1, ow1.getOuterIdentity());
+
+ hardReferences.add(ow1);
+ }
+
+ {
+ DefaultObjectWrapperBuilder factory = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ factory.setExposeFields(true);
+ DefaultObjectWrapper ow1 = factory.build();
+ DefaultObjectWrapper ow2 = factory.build();
+ assertEquals(4, getDefaultObjectWrapperInstanceCacheSize());
+ assertSame(ow1, ow2);
+
+ assertSame(ow1.getClass(), DefaultObjectWrapper.class);
+ assertEquals(Configuration.VERSION_3_0_0, ow1.getIncompatibleImprovements());
+ assertTrue(ow1.isWriteProtected());
+ assertTrue(ow1.isExposeFields());
+
+ hardReferences.add(ow1);
+ }
+
+ {
+ DefaultObjectWrapperBuilder factory = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ factory.setStrict(true);
+ factory.setDefaultDateType(TemplateDateModel.DATETIME);
+ factory.setOuterIdentity(new SimpleObjectWrapper(Configuration.VERSION_3_0_0));
+ DefaultObjectWrapper ow = factory.build();
+ assertEquals(5, getDefaultObjectWrapperInstanceCacheSize());
+ assertTrue(ow.isStrict());
+ assertEquals(TemplateDateModel.DATETIME, ow.getDefaultDateType());
+ assertSame(SimpleObjectWrapper.class, ow.getOuterIdentity().getClass());
+
+ hardReferences.add(ow);
+ }
+
+ // Effect of reference and cache clearings:
+ {
+ DefaultObjectWrapper bw1 = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ assertEquals(5, getDefaultObjectWrapperInstanceCacheSize());
+ assertEquals(5, getDefaultObjectWrapperNonClearedInstanceCacheSize());
+
+ clearDefaultObjectWrapperInstanceCacheReferences(false);
+ assertEquals(5, getDefaultObjectWrapperInstanceCacheSize());
+ assertEquals(0, getDefaultObjectWrapperNonClearedInstanceCacheSize());
+
+ DefaultObjectWrapper bw2 = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ assertNotSame(bw1, bw2);
+ assertEquals(5, getDefaultObjectWrapperInstanceCacheSize());
+ assertEquals(1, getDefaultObjectWrapperNonClearedInstanceCacheSize());
+
+ assertSame(bw2, new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build());
+ assertEquals(1, getDefaultObjectWrapperNonClearedInstanceCacheSize());
+
+ clearDefaultObjectWrapperInstanceCacheReferences(true);
+ DefaultObjectWrapper bw3 = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ assertNotSame(bw2, bw3);
+ assertEquals(1, getDefaultObjectWrapperInstanceCacheSize());
+ assertEquals(1, getDefaultObjectWrapperNonClearedInstanceCacheSize());
+ }
+
+ {
+ DefaultObjectWrapperBuilder factory = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ factory.setUseModelCache(true);
+ DefaultObjectWrapper ow = factory.build();
+ assertTrue(ow.getUseModelCache());
+ assertEquals(2, getDefaultObjectWrapperInstanceCacheSize());
+
+ hardReferences.add(ow);
+ }
+
+ assertTrue(hardReferences.size() != 0); // just to save it from GC until this line
+ }
+
+ private DefaultObjectWrapper getDefaultObjectWrapperWithSetting(Version ici, boolean useModelCache) {
+ DefaultObjectWrapperBuilder f = new DefaultObjectWrapperBuilder(ici);
+ f.setUseModelCache(useModelCache);
+ return f.build();
+ }
+
+ public void testMultipleTCCLs() {
+ List<DefaultObjectWrapper> hardReferences = new LinkedList<>();
+
+ assertEquals(0, getDefaultObjectWrapperInstanceCacheSize());
+
+ DefaultObjectWrapper bw1 = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ assertEquals(1, getDefaultObjectWrapperInstanceCacheSize());
+ hardReferences.add(bw1);
+
+ ClassLoader oldTCCL = Thread.currentThread().getContextClassLoader();
+ // Doesn't mater what, just be different from oldTCCL:
+ ClassLoader newTCCL = oldTCCL == null ? getClass().getClassLoader() : null;
+
+ DefaultObjectWrapper bw2;
+ Thread.currentThread().setContextClassLoader(newTCCL);
+ try {
+ bw2 = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ assertEquals(2, getDefaultObjectWrapperInstanceCacheSize());
+ hardReferences.add(bw2);
+
+ assertNotSame(bw1, bw2);
+ assertSame(bw2, new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build());
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldTCCL);
+ }
+
+ assertSame(bw1, new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build());
+ assertEquals(2, getDefaultObjectWrapperInstanceCacheSize());
+
+ DefaultObjectWrapper bw3;
+ Thread.currentThread().setContextClassLoader(newTCCL);
+ try {
+ assertSame(bw2, new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build());
+
+ DefaultObjectWrapperBuilder bwb = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ bwb.setExposeFields(true);
+ bw3 = bwb.build();
+ assertEquals(3, getDefaultObjectWrapperInstanceCacheSize());
+ hardReferences.add(bw3);
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldTCCL);
+ }
+
+ {
+ DefaultObjectWrapperBuilder bwb = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ bwb.setExposeFields(true);
+ DefaultObjectWrapper bw4 = bwb.build();
+ assertEquals(4, getDefaultObjectWrapperInstanceCacheSize());
+ assertNotSame(bw3, bw4);
+ hardReferences.add(bw4);
+ }
+
+ assertTrue(hardReferences.size() != 0); // just to save it from GC until this line
+ }
+
+ public void testClassInrospectorCache() throws TemplateModelException {
+ assertFalse(new DefaultObjectWrapper(Configuration.VERSION_3_0_0).isClassIntrospectionCacheRestricted());
+ assertTrue(new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0)
+ .build().isClassIntrospectionCacheRestricted());
+
+ ClassIntrospectorBuilder.clearInstanceCache();
+ DefaultObjectWrapperBuilder.clearInstanceCache();
+ checkClassIntrospectorCacheSize(0);
+
+ List<DefaultObjectWrapper> hardReferences = new LinkedList<>();
+ DefaultObjectWrapperBuilder builder;
+
+ {
+ builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+
+ DefaultObjectWrapper bw1 = builder.build();
+ checkClassIntrospectorCacheSize(1);
+
+ builder.setExposureLevel(DefaultObjectWrapper.EXPOSE_SAFE); // this was already set to this
+ builder.setUseModelCache(true); // this shouldn't matter for the introspection cache
+ DefaultObjectWrapper bw2 = builder.build();
+ checkClassIntrospectorCacheSize(1);
+
+ assertSame(bw2.getClassIntrospector(), bw1.getClassIntrospector());
+ assertNotSame(bw1, bw2);
+
+ // Wrapping tests:
+ assertFalse(exposesFields(bw1));
+ assertTrue(exposesProperties(bw1));
+ assertTrue(exposesMethods(bw1));
+ assertFalse(exposesUnsafe(bw1));
+ assertTrue(bw1.isClassIntrospectionCacheRestricted());
+ // Prevent introspection cache GC:
+ hardReferences.add(bw1);
+ hardReferences.add(bw2);
+ }
+
+ {
+ builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ builder.setExposeFields(true);
+ DefaultObjectWrapper ow = builder.build();
+ checkClassIntrospectorCacheSize(2);
+ // Wrapping tests:
+ assertTrue(exposesFields(ow));
+ assertTrue(exposesProperties(ow));
+ assertTrue(exposesMethods(ow));
+ assertFalse(exposesUnsafe(ow));
+ // Prevent introspection cache GC:
+ hardReferences.add(ow);
+ }
+
+ {
+ builder.setExposureLevel(DefaultObjectWrapper.EXPOSE_ALL);
+ DefaultObjectWrapper ow = builder.build();
+ checkClassIntrospectorCacheSize(3);
+ // Wrapping tests:
+ assertTrue(exposesFields(ow));
+ assertTrue(exposesProperties(ow));
+ assertTrue(exposesMethods(ow));
+ assertTrue(exposesUnsafe(ow));
+ // Prevent introspection cache GC:
+ hardReferences.add(ow);
+ }
+
+ {
+ builder.setExposeFields(false);
+ DefaultObjectWrapper ow = builder.build();
+ checkClassIntrospectorCacheSize(4);
+ // Wrapping tests:
+ assertFalse(exposesFields(ow));
+ assertTrue(exposesProperties(ow));
+ assertTrue(exposesMethods(ow));
+ assertTrue(exposesUnsafe(ow));
+ // Prevent introspection cache GC:
+ hardReferences.add(ow);
+ }
+
+ {
+ builder.setExposureLevel(DefaultObjectWrapper.EXPOSE_NOTHING);
+ DefaultObjectWrapper ow = builder.build();
+ checkClassIntrospectorCacheSize(5);
+ // Wrapping tests:
+ assertFalse(exposesFields(ow));
+ assertFalse(exposesProperties(ow));
+ assertFalse(exposesMethods(ow));
+ assertFalse(exposesUnsafe(ow));
+ // Prevent introspection cache GC:
+ hardReferences.add(ow);
+ }
+
+ {
+ builder.setExposeFields(true);
+ DefaultObjectWrapper ow = builder.build();
+ checkClassIntrospectorCacheSize(6);
+ // Wrapping tests:
+ assertTrue(exposesFields(ow));
+ assertFalse(exposesProperties(ow));
+ assertFalse(exposesMethods(ow));
+ assertFalse(exposesUnsafe(ow));
+ // Prevent introspection cache GC:
+ hardReferences.add(ow);
+ }
+
+ {
+ builder.setExposureLevel(DefaultObjectWrapper.EXPOSE_PROPERTIES_ONLY);
+ DefaultObjectWrapper ow = builder.build();
+ checkClassIntrospectorCacheSize(7);
+ // Wrapping tests:
+ assertTrue(exposesFields(ow));
+ assertTrue(exposesProperties(ow));
+ assertFalse(exposesMethods(ow));
+ assertFalse(exposesUnsafe(ow));
+ // Prevent introspection cache GC:
+ hardReferences.add(ow);
+ }
+
+ {
+ builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ builder.setUseModelCache(true);
+ builder.setExposeFields(false);
+ builder.setExposureLevel(DefaultObjectWrapper.EXPOSE_PROPERTIES_ONLY);
+
+ DefaultObjectWrapper bw1 = builder.build();
+ checkClassIntrospectorCacheSize(8);
+ ClassIntrospector ci1 = bw1.getClassIntrospector();
+
+ builder.setUseModelCache(false); // Shouldn't mater for the ClassIntrospector
+ DefaultObjectWrapper bw2 = builder.build();
+ ClassIntrospector ci2 = bw2.getClassIntrospector();
+ checkClassIntrospectorCacheSize(8);
+
+ assertSame(ci1, ci2);
+ assertNotSame(bw1, bw2);
+
+ // Wrapping tests:
+ assertFalse(exposesFields(bw1));
+ assertTrue(exposesProperties(bw1));
+ assertFalse(exposesMethods(bw1));
+ assertFalse(exposesUnsafe(bw1));
+
+ // Prevent introspection cache GC:
+ hardReferences.add(bw1);
+ hardReferences.add(bw2);
+ }
+
+ // The ClassInrospector cache couldn't become cleared in reality otherwise:
+ DefaultObjectWrapperBuilder.clearInstanceCache();
+
+ clearClassIntrospectorInstanceCacheReferences(false);
+ checkClassIntrospectorCacheSize(8);
+ assertEquals(0, getClassIntrospectorNonClearedInstanceCacheSize());
+
+ {
+ builder.setExposeFields(false);
+
+ DefaultObjectWrapper bw1 = builder.build();
+ checkClassIntrospectorCacheSize(8);
+ assertEquals(1, getClassIntrospectorNonClearedInstanceCacheSize());
+ ClassIntrospector ci1 = bw1.getClassIntrospector();
+
+ builder.setUseModelCache(true); // Shouldn't mater
+ DefaultObjectWrapper bw2 = builder.build();
+ ClassIntrospector ci2 = bw2.getClassIntrospector();
+
+ assertSame(ci1, ci2);
+ assertNotSame(bw1, bw2);
+
+ // Wrapping tests:
+ assertFalse(exposesFields(bw1));
+ assertTrue(exposesProperties(bw1));
+ assertFalse(exposesMethods(bw1));
+ assertFalse(exposesUnsafe(bw1));
+
+ // Prevent introspection cache GC:
+ hardReferences.add(bw1);
+ hardReferences.add(bw2);
+ }
+
+ {
+ builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ DefaultObjectWrapper ow = builder.build();
+ checkClassIntrospectorCacheSize(8);
+ assertEquals(2, getClassIntrospectorNonClearedInstanceCacheSize());
+ // Wrapping tests:
+ assertFalse(exposesFields(ow));
+ assertTrue(exposesProperties(ow));
+ assertTrue(exposesMethods(ow));
+ assertFalse(exposesUnsafe(ow));
+ // Prevent introspection cache GC:
+ hardReferences.add(ow);
+ }
+
+ clearClassIntrospectorInstanceCacheReferences(true);
+ checkClassIntrospectorCacheSize(8);
+ assertEquals(0, getClassIntrospectorNonClearedInstanceCacheSize());
+
+ {
+ builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ builder.setExposeFields(true);
+ DefaultObjectWrapper ow = builder.build();
+ checkClassIntrospectorCacheSize(1);
+ // Wrapping tests:
+ assertTrue(exposesFields(ow));
+ assertTrue(exposesProperties(ow));
+ assertTrue(exposesMethods(ow));
+ assertFalse(exposesUnsafe(ow));
+ // Prevent introspection cache GC:
+ hardReferences.add(ow);
+ }
+
+ {
+ builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ builder.setMethodAppearanceFineTuner(new MethodAppearanceFineTuner() {
+ @Override
+ public void process(DecisionInput in, Decision out) {
+ }
+ }); // spoils ClassIntrospector() sharing
+
+ builder.setUseModelCache(false);
+ DefaultObjectWrapper bw1 = builder.build();
+ assertSame(bw1, builder.build());
+
+ builder.setUseModelCache(true);
+ DefaultObjectWrapper bw2 = builder.build();
+ checkClassIntrospectorCacheSize(1);
+ assertNotSame(bw1, bw2);
+ assertNotSame(bw1.getClassIntrospector(), bw2.getClassIntrospector());
+ assertTrue(bw1.isClassIntrospectionCacheRestricted());
+ assertTrue(bw2.isClassIntrospectionCacheRestricted());
+ }
+
+ {
+ builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ builder.setMethodAppearanceFineTuner(
+ GetlessMethodsAsPropertyGettersRule.INSTANCE); // doesn't spoils sharing
+
+ builder.setUseModelCache(false);
+ DefaultObjectWrapper bw1 = builder.build();
+ assertSame(bw1, builder.build());
+ checkClassIntrospectorCacheSize(2);
+
+ builder.setUseModelCache(true);
+ DefaultObjectWrapper bw2 = builder.build();
+ checkClassIntrospectorCacheSize(2);
+
+ assertNotSame(bw1, bw2);
+ assertSame(bw1.getClassIntrospector(), bw2.getClassIntrospector()); // !
+ assertTrue(bw2.isClassIntrospectionCacheRestricted());
+ }
+
+ assertTrue(hardReferences.size() != 0); // just to save it from GC until this line
+ }
+
+ private void checkClassIntrospectorCacheSize(int expectedSize) {
+ assertEquals(expectedSize, getClassIntrospectorInstanceCacheSize());
+ }
+
+ private void assertNotEquals(Object o1, Object o2) {
+ assertFalse(o1.equals(o2));
+ }
+
+ public class C {
+
+ public String foo = "FOO";
+
+ public String getBar() {
+ return "BAR";
+ }
+
+ }
+
+ private boolean exposesFields(DefaultObjectWrapper ow) throws TemplateModelException {
+ TemplateHashModel thm = (TemplateHashModel) ow.wrap(new C());
+ TemplateScalarModel r = (TemplateScalarModel) thm.get("foo");
+ if (r == null) return false;
+ assertEquals("FOO", r.getAsString());
+ return true;
+ }
+
+ private boolean exposesProperties(DefaultObjectWrapper ow) throws TemplateModelException {
+ TemplateHashModel thm = (TemplateHashModel) ow.wrap(new C());
+ TemplateScalarModel r = (TemplateScalarModel) thm.get("bar");
+ if (r == null) return false;
+ assertEquals("BAR", r.getAsString());
+ return true;
+ }
+
+ private boolean exposesMethods(DefaultObjectWrapper ow) throws TemplateModelException {
+ TemplateHashModel thm = (TemplateHashModel) ow.wrap(new C());
+ return thm.get("getBar") != null;
+ }
+
+ private boolean exposesUnsafe(DefaultObjectWrapper ow) throws TemplateModelException {
+ TemplateHashModel thm = (TemplateHashModel) ow.wrap(new C());
+ return thm.get("wait") != null;
+ }
+
+ static int getClassIntrospectorInstanceCacheSize() {
+ Map instanceCache = ClassIntrospectorBuilder.getInstanceCache();
+ synchronized (instanceCache) {
+ return instanceCache.size();
+ }
+ }
+
+ static int getClassIntrospectorNonClearedInstanceCacheSize() {
+ Map instanceCache = ClassIntrospectorBuilder.getInstanceCache();
+ synchronized (instanceCache) {
+ int cnt = 0;
+ for (Iterator it = instanceCache.values().iterator(); it.hasNext(); ) {
+ if (((Reference) it.next()).get() != null) cnt++;
+ }
+ return cnt;
+ }
+ }
+
+ static void clearClassIntrospectorInstanceCacheReferences(boolean enqueue) {
+ Map instanceCache = ClassIntrospectorBuilder.getInstanceCache();
+ synchronized (instanceCache) {
+ for (Iterator it = instanceCache.values().iterator(); it.hasNext(); ) {
+ Reference ref = ((Reference) it.next());
+ ref.clear();
+ if (enqueue) {
+ ref.enqueue();
+ }
+ }
+ }
+ }
+
+ static int getDefaultObjectWrapperInstanceCacheSize() {
+ Map instanceCache = DefaultObjectWrapperBuilder.getInstanceCache();
+ synchronized (instanceCache) {
+ int size = 0;
+ for (Iterator it1 = instanceCache.values().iterator(); it1.hasNext(); ) {
+ size += ((Map) it1.next()).size();
+ }
+ return size;
+ }
+ }
+
+ static int getDefaultObjectWrapperNonClearedInstanceCacheSize() {
+ Map instanceCache = DefaultObjectWrapperBuilder.getInstanceCache();
+ synchronized (instanceCache) {
+ int cnt = 0;
+ for (Iterator it1 = instanceCache.values().iterator(); it1.hasNext(); ) {
+ Map tcclScope = (Map) it1.next();
+ for (Iterator it2 = tcclScope.values().iterator(); it2.hasNext(); ) {
+ if (((Reference) it2.next()).get() != null) cnt++;
+ }
+ }
+ return cnt;
+ }
+ }
+
+ static void clearDefaultObjectWrapperInstanceCacheReferences(boolean enqueue) {
+ Map instanceCache = DefaultObjectWrapperBuilder.getInstanceCache();
+ synchronized (instanceCache) {
+ for (Iterator it1 = instanceCache.values().iterator(); it1.hasNext(); ) {
+ Map tcclScope = (Map) it1.next();
+ for (Iterator it2 = tcclScope.values().iterator(); it2.hasNext(); ) {
+ Reference ref = ((Reference) it2.next());
+ ref.clear();
+ if (enqueue) {
+ ref.enqueue();
+ }
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
index 48099a7..5b62a9b 100644
--- a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
+++ b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
@@ -39,6 +39,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
+import java.util.Vector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -77,9 +78,10 @@ import com.google.common.collect.ImmutableMap;
public class DefaultObjectWrapperTest {
- private final static DefaultObjectWrapper OW300 = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0)
+ private final static DefaultObjectWrapper OW = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0)
.build();
+ // This will make sense if we will have multipe incompatibleImprovement versions.
@Test
public void testIncompatibleImprovementsVersionBreakPoints() throws Exception {
List<Version> expected = new ArrayList<>();
@@ -135,22 +137,26 @@ public class DefaultObjectWrapperTest {
@SuppressWarnings("boxing")
@Test
public void testBuilder() throws Exception {
- for (boolean simpleMapWrapper : new boolean[] { true, false }) {
- DefaultObjectWrapperBuilder builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
- builder.setSimpleMapWrapper(simpleMapWrapper); // Shouldn't mater
- DefaultObjectWrapper bw = builder.build();
- assertSame(bw, builder.build());
- assertSame(bw.getClass(), DefaultObjectWrapper.class);
- assertEquals(Configuration.VERSION_3_0_0, bw.getIncompatibleImprovements());
- assertTrue(bw.isWriteProtected());
- assertEquals(simpleMapWrapper, bw.isSimpleMapWrapper());
-
- assertThat(bw.wrap(new HashMap()), instanceOf(DefaultMapAdapter.class));
- assertThat(bw.wrap(new ArrayList()), instanceOf(DefaultListAdapter.class));
- assertThat(bw.wrap(new String[] {}), instanceOf(DefaultArrayAdapter.class));
- assertThat(bw.wrap(new HashSet()), instanceOf(DefaultNonListCollectionAdapter.class));
- assertThat(bw.wrap(new PureIterable()), instanceOf(DefaultIterableAdapter.class));
- }
+ DefaultObjectWrapperBuilder builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ DefaultObjectWrapper ow = builder.build();
+ assertSame(ow, builder.build());
+ assertSame(ow.getClass(), DefaultObjectWrapper.class);
+ assertEquals(Configuration.VERSION_3_0_0, ow.getIncompatibleImprovements());
+ assertTrue(ow.isWriteProtected());
+ }
+
+ @Test
+ public void testWrappedTypes() throws Exception {
+ DefaultObjectWrapperBuilder builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0);
+ DefaultObjectWrapper ow = builder.build();
+
+ assertThat(ow.wrap(new HashMap()), instanceOf(DefaultMapAdapter.class));
+ assertThat(ow.wrap(new ArrayList()), instanceOf(DefaultListAdapter.class));
+ assertThat(ow.wrap(new String[] {}), instanceOf(DefaultArrayAdapter.class));
+ assertThat(ow.wrap(new HashSet()), instanceOf(DefaultNonListCollectionAdapter.class));
+ assertThat(ow.wrap(new PureIterable()), instanceOf(DefaultIterableAdapter.class));
+ assertThat(ow.wrap(new Vector<>().iterator()), instanceOf(DefaultIteratorAdapter.class));
+ assertThat(ow.wrap(new Vector<>().elements()), instanceOf(DefaultEnumerationAdapter.class));
}
@Test
@@ -218,8 +224,8 @@ public class DefaultObjectWrapperTest {
@SuppressWarnings("boxing")
@Test
- public void testRoundtripping() throws TemplateModelException, ClassNotFoundException {
- DefaultObjectWrapper dow22 = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
+ public void testCompositeValueWrapping() throws TemplateModelException, ClassNotFoundException {
+ DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
final Map hashMap = new HashMap();
inintTestMap(hashMap);
@@ -234,13 +240,16 @@ public class DefaultObjectWrapperTest {
linkedList.add("c");
final int[] intArray = new int[] { 1, 2, 3 };
final String[] stringArray = new String[] { "a", "b", "c" };
+ final PureIterable pureIterable = new PureIterable();
+ final HashSet hashSet = new HashSet();
- assertRoundtrip(dow22, linkedHashMap, DefaultMapAdapter.class, LinkedHashMap.class, linkedHashMap.toString());
- assertRoundtrip(dow22, treeMap, DefaultMapAdapter.class, TreeMap.class, treeMap.toString());
- assertRoundtrip(dow22, gMap, DefaultMapAdapter.class, ImmutableMap.class, gMap.toString());
- assertRoundtrip(dow22, linkedList, DefaultListAdapter.class, LinkedList.class, linkedList.toString());
- assertRoundtrip(dow22, intArray, DefaultArrayAdapter.class, int[].class, null);
- assertRoundtrip(dow22, stringArray, DefaultArrayAdapter.class, String[].class, null);
+ assertRoundtrip(ow, linkedHashMap, DefaultMapAdapter.class, LinkedHashMap.class, linkedHashMap.toString());
+ assertRoundtrip(ow, treeMap, DefaultMapAdapter.class, TreeMap.class, treeMap.toString());
+ assertRoundtrip(ow, gMap, DefaultMapAdapter.class, ImmutableMap.class, gMap.toString());
+ assertRoundtrip(ow, linkedList, DefaultListAdapter.class, LinkedList.class, linkedList.toString());
+ assertRoundtrip(ow, intArray, DefaultArrayAdapter.class, int[].class, null);
+ assertRoundtrip(ow, stringArray, DefaultArrayAdapter.class, String[].class, null);
+ assertRoundtrip(ow, hashSet, DefaultNonListCollectionAdapter.class, HashSet.class, hashSet.toString());
}
@SuppressWarnings("boxing")
@@ -260,7 +269,7 @@ public class DefaultObjectWrapperTest {
testMap.put("d", Collections.singletonList("x"));
{
- TemplateHashModelEx hash = (TemplateHashModelEx) OW300.wrap(testMap);
+ TemplateHashModelEx hash = (TemplateHashModelEx) OW.wrap(testMap);
assertEquals(4, hash.size());
assertFalse(hash.isEmpty());
assertNull(hash.get("e"));
@@ -276,7 +285,7 @@ public class DefaultObjectWrapperTest {
}
{
- assertTrue(((TemplateHashModel) OW300.wrap(Collections.emptyMap())).isEmpty());
+ assertTrue(((TemplateHashModel) OW.wrap(Collections.emptyMap())).isEmpty());
}
}
@@ -290,14 +299,14 @@ public class DefaultObjectWrapperTest {
if (idx >= expectedItems.length) {
fail("Number of items is more than the expected " + expectedItems.length);
}
- assertEquals(expectedItems[idx], OW300.unwrap(actualItem));
+ assertEquals(expectedItems[idx], OW.unwrap(actualItem));
if (i == 1) {
// In the 2nd round we also test with two iterators in parallel.
// This 2nd iterator is also special in that its hasNext() is never called.
if (it2 == null) {
it2 = coll.iterator();
}
- assertEquals(expectedItems[idx], OW300.unwrap(it2.next()));
+ assertEquals(expectedItems[idx], OW.unwrap(it2.next()));
}
idx++;
}
@@ -317,7 +326,7 @@ public class DefaultObjectWrapperTest {
testList.add("c");
testList.add(new String[] { "x" });
- TemplateSequenceModel seq = (TemplateSequenceModel) OW300.wrap(testList);
+ TemplateSequenceModel seq = (TemplateSequenceModel) OW.wrap(testList);
assertTrue(seq instanceof DefaultListAdapter);
assertFalse(seq instanceof TemplateCollectionModel); // Maybe changes at 2.4.0
assertEquals(4, seq.size());
@@ -337,7 +346,7 @@ public class DefaultObjectWrapperTest {
testList.add(null);
testList.add("c");
- TemplateSequenceModel seq = (TemplateSequenceModel) OW300.wrap(testList);
+ TemplateSequenceModel seq = (TemplateSequenceModel) OW.wrap(testList);
assertTrue(seq instanceof DefaultListAdapter);
assertTrue(seq instanceof TemplateCollectionModel); // Maybe changes at 2.4.0
assertEquals(3, seq.size());
@@ -364,16 +373,16 @@ public class DefaultObjectWrapperTest {
@Test
public void testArrayAdapterTypes() throws TemplateModelException {
- assertArrayAdapterClass("Object", OW300.wrap(new Object[] {}));
- assertArrayAdapterClass("Object", OW300.wrap(new String[] {}));
- assertArrayAdapterClass("byte", OW300.wrap(new byte[] {}));
- assertArrayAdapterClass("short", OW300.wrap(new short[] {}));
- assertArrayAdapterClass("int", OW300.wrap(new int[] {}));
- assertArrayAdapterClass("long", OW300.wrap(new long[] {}));
- assertArrayAdapterClass("float", OW300.wrap(new float[] {}));
- assertArrayAdapterClass("double", OW300.wrap(new double[] {}));
- assertArrayAdapterClass("boolean", OW300.wrap(new boolean[] {}));
- assertArrayAdapterClass("char", OW300.wrap(new char[] {}));
+ assertArrayAdapterClass("Object", OW.wrap(new Object[] {}));
+ assertArrayAdapterClass("Object", OW.wrap(new String[] {}));
+ assertArrayAdapterClass("byte", OW.wrap(new byte[] {}));
+ assertArrayAdapterClass("short", OW.wrap(new short[] {}));
+ assertArrayAdapterClass("int", OW.wrap(new int[] {}));
+ assertArrayAdapterClass("long", OW.wrap(new long[] {}));
+ assertArrayAdapterClass("float", OW.wrap(new float[] {}));
+ assertArrayAdapterClass("double", OW.wrap(new double[] {}));
+ assertArrayAdapterClass("boolean", OW.wrap(new boolean[] {}));
+ assertArrayAdapterClass("char", OW.wrap(new char[] {}));
}
private void assertArrayAdapterClass(String adapterCompType, TemplateModel adaptedArray) {
@@ -388,7 +397,7 @@ public class DefaultObjectWrapperTest {
{
final String[] testArray = new String[] { "a", null, "c" };
- TemplateSequenceModel seq = (TemplateSequenceModel) OW300.wrap(testArray);
+ TemplateSequenceModel seq = (TemplateSequenceModel) OW.wrap(testArray);
assertEquals(3, seq.size());
assertNull(seq.get(-1));
assertEquals("a", ((TemplateScalarModel) seq.get(0)).getAsString());
@@ -399,7 +408,7 @@ public class DefaultObjectWrapperTest {
{
final int[] testArray = new int[] { 11, 22 };
- TemplateSequenceModel seq = (TemplateSequenceModel) OW300.wrap(testArray);
+ TemplateSequenceModel seq = (TemplateSequenceModel) OW.wrap(testArray);
assertEquals(2, seq.size());
assertNull(seq.get(-1));
assertEqualsAndSameClass(Integer.valueOf(11), ((TemplateNumberModel) seq.get(0)).getAsNumber());
@@ -409,7 +418,7 @@ public class DefaultObjectWrapperTest {
{
final double[] testArray = new double[] { 11, 22 };
- TemplateSequenceModel seq = (TemplateSequenceModel) OW300.wrap(testArray);
+ TemplateSequenceModel seq = (TemplateSequenceModel) OW.wrap(testArray);
assertEquals(2, seq.size());
assertNull(seq.get(-1));
assertEqualsAndSameClass(Double.valueOf(11), ((TemplateNumberModel) seq.get(0)).getAsNumber());
@@ -419,7 +428,7 @@ public class DefaultObjectWrapperTest {
{
final boolean[] testArray = new boolean[] { true, false };
- TemplateSequenceModel seq = (TemplateSequenceModel) OW300.wrap(testArray);
+ TemplateSequenceModel seq = (TemplateSequenceModel) OW.wrap(testArray);
assertEquals(2, seq.size());
assertNull(seq.get(-1));
assertEqualsAndSameClass(Boolean.valueOf(true), ((TemplateBooleanModel) seq.get(0)).getAsBoolean());
@@ -429,7 +438,7 @@ public class DefaultObjectWrapperTest {
{
final char[] testArray = new char[] { 'a', 'b' };
- TemplateSequenceModel seq = (TemplateSequenceModel) OW300.wrap(testArray);
+ TemplateSequenceModel seq = (TemplateSequenceModel) OW.wrap(testArray);
assertEquals(2, seq.size());
assertNull(seq.get(-1));
assertEquals("a", ((TemplateScalarModel) seq.get(0)).getAsString());
@@ -476,25 +485,25 @@ public class DefaultObjectWrapperTest {
set.add("a");
set.add("b");
set.add("c");
- TemplateCollectionModelEx coll = (TemplateCollectionModelEx) OW300.wrap(set);
+ TemplateCollectionModelEx coll = (TemplateCollectionModelEx) OW.wrap(set);
assertTrue(coll instanceof DefaultNonListCollectionAdapter);
assertEquals(3, coll.size());
assertFalse(coll.isEmpty());
assertCollectionTMEquals(coll, "a", "b", "c");
- assertTrue(coll.contains(OW300.wrap("a")));
- assertTrue(coll.contains(OW300.wrap("b")));
- assertTrue(coll.contains(OW300.wrap("c")));
- assertTrue(coll.contains(OW300.wrap("c")));
- assertFalse(coll.contains(OW300.wrap("d")));
+ assertTrue(coll.contains(OW.wrap("a")));
+ assertTrue(coll.contains(OW.wrap("b")));
+ assertTrue(coll.contains(OW.wrap("c")));
+ assertTrue(coll.contains(OW.wrap("c")));
+ assertFalse(coll.contains(OW.wrap("d")));
try {
- assertFalse(coll.contains(OW300.wrap(1)));
+ assertFalse(coll.contains(OW.wrap(1)));
fail();
} catch (TemplateModelException e) {
assertThat(e.getMessage(), containsString("Integer"));
}
- assertRoundtrip(OW300, set, DefaultNonListCollectionAdapter.class, TreeSet.class, "[a, b, c]");
+ assertRoundtrip(OW, set, DefaultNonListCollectionAdapter.class, TreeSet.class, "[a, b, c]");
assertSizeThroughAPIModel(3, coll);
}
@@ -504,24 +513,24 @@ public class DefaultObjectWrapperTest {
final List<String> list = Collections.singletonList("b");
set.add(list);
set.add(null);
- TemplateCollectionModelEx coll = (TemplateCollectionModelEx) OW300.wrap(set);
+ TemplateCollectionModelEx coll = (TemplateCollectionModelEx) OW.wrap(set);
TemplateModelIterator it = coll.iterator();
final TemplateModel tm1 = it.next();
- Object obj1 = OW300.unwrap(tm1);
+ Object obj1 = OW.unwrap(tm1);
final TemplateModel tm2 = it.next();
- Object obj2 = OW300.unwrap(tm2);
+ Object obj2 = OW.unwrap(tm2);
assertTrue(obj1 == null || obj2 == null);
assertTrue(obj1 != null && obj1.equals(list) || obj2 != null && obj2.equals(list));
assertTrue(tm1 instanceof DefaultListAdapter || tm2 instanceof DefaultListAdapter);
List similarList = new ArrayList();
similarList.add("b");
- assertTrue(coll.contains(OW300.wrap(similarList)));
- assertTrue(coll.contains(OW300.wrap(null)));
- assertFalse(coll.contains(OW300.wrap("a")));
- assertFalse(coll.contains(OW300.wrap(1)));
+ assertTrue(coll.contains(OW.wrap(similarList)));
+ assertTrue(coll.contains(OW.wrap(null)));
+ assertFalse(coll.contains(OW.wrap("a")));
+ assertFalse(coll.contains(OW.wrap(1)));
- assertRoundtrip(OW300, set, DefaultNonListCollectionAdapter.class, HashSet.class, "[" + obj1 + ", "
+ assertRoundtrip(OW, set, DefaultNonListCollectionAdapter.class, HashSet.class, "[" + obj1 + ", "
+ obj2 + "]");
}
}
@@ -531,14 +540,14 @@ public class DefaultObjectWrapperTest {
public void testCollectionAdapterOutOfBounds() throws TemplateModelException {
Set set = Collections.singleton(123);
- TemplateCollectionModelEx coll = (TemplateCollectionModelEx) OW300.wrap(set);
+ TemplateCollectionModelEx coll = (TemplateCollectionModelEx) OW.wrap(set);
TemplateModelIterator it = coll.iterator();
for (int i = 0; i < 3; i++) {
assertTrue(it.hasNext());
}
- assertEquals(123, OW300.unwrap(it.next()));
+ assertEquals(123, OW.unwrap(it.next()));
for (int i = 0; i < 3; i++) {
assertFalse(it.hasNext());
@@ -556,7 +565,7 @@ public class DefaultObjectWrapperTest {
Set set = new HashSet();
set.add(null);
- TemplateCollectionModelEx coll = (TemplateCollectionModelEx) OW300.wrap(set);
+ TemplateCollectionModelEx coll = (TemplateCollectionModelEx) OW.wrap(set);
assertEquals(1, coll.size());
assertFalse(coll.isEmpty());
assertNull(coll.iterator().next());
@@ -566,18 +575,18 @@ public class DefaultObjectWrapperTest {
public void testIteratorWrapping() throws TemplateModelException, ClassNotFoundException {
final List<String> list = ImmutableList.of("a", "b", "c");
Iterator<String> it = list.iterator();
- TemplateCollectionModel coll = (TemplateCollectionModel) OW300.wrap(it);
+ TemplateCollectionModel coll = (TemplateCollectionModel) OW.wrap(it);
- assertRoundtrip(OW300, coll, DefaultIteratorAdapter.class, Iterator.class, null);
+ assertRoundtrip(OW, coll, DefaultIteratorAdapter.class, Iterator.class, null);
TemplateModelIterator itIt = coll.iterator();
TemplateModelIterator itIt2 = coll.iterator(); // used later
assertTrue(itIt.hasNext());
- assertEquals("a", OW300.unwrap(itIt.next()));
+ assertEquals("a", OW.unwrap(itIt.next()));
assertTrue(itIt.hasNext());
- assertEquals("b", OW300.unwrap(itIt.next()));
+ assertEquals("b", OW.unwrap(itIt.next()));
assertTrue(itIt.hasNext());
- assertEquals("c", OW300.unwrap(itIt.next()));
+ assertEquals("c", OW.unwrap(itIt.next()));
assertFalse(itIt.hasNext());
try {
itIt.next();
@@ -614,20 +623,20 @@ public class DefaultObjectWrapperTest {
Map sortedMapC = new TreeMap<>();
sortedMapC.put('a', 1);
- assertEquals(1, OW300.unwrap(((TemplateHashModel) OW300.wrap(hashMapS)).get("a")));
- assertEquals(1, OW300.unwrap(((TemplateHashModel) OW300.wrap(hashMapC)).get("a")));
- assertEquals(1, OW300.unwrap(((TemplateHashModel) OW300.wrap(sortedMapS)).get("a")));
+ assertEquals(1, OW.unwrap(((TemplateHashModel) OW.wrap(hashMapS)).get("a")));
+ assertEquals(1, OW.unwrap(((TemplateHashModel) OW.wrap(hashMapC)).get("a")));
+ assertEquals(1, OW.unwrap(((TemplateHashModel) OW.wrap(sortedMapS)).get("a")));
try {
- ((TemplateHashModel) OW300.wrap(sortedMapC)).get("a");
+ ((TemplateHashModel) OW.wrap(sortedMapC)).get("a");
} catch (TemplateModelException e) {
assertThat(e.getMessage(), containsStringIgnoringCase("String key"));
}
- assertNull(((TemplateHashModel) OW300.wrap(hashMapS)).get("b"));
- assertNull(((TemplateHashModel) OW300.wrap(hashMapC)).get("b"));
- assertNull(((TemplateHashModel) OW300.wrap(sortedMapS)).get("b"));
+ assertNull(((TemplateHashModel) OW.wrap(hashMapS)).get("b"));
+ assertNull(((TemplateHashModel) OW.wrap(hashMapC)).get("b"));
+ assertNull(((TemplateHashModel) OW.wrap(sortedMapS)).get("b"));
try {
- ((TemplateHashModel) OW300.wrap(sortedMapC)).get("b");
+ ((TemplateHashModel) OW.wrap(sortedMapC)).get("b");
} catch (TemplateModelException e) {
assertThat(e.getMessage(), containsStringIgnoringCase("String key"));
}
@@ -639,7 +648,7 @@ public class DefaultObjectWrapperTest {
String listingFTL = "<#list value as x>${x}<#sep>, </#list>";
- DefaultObjectWrapper ow = OW300;
+ DefaultObjectWrapper ow = OW;
TemplateModel tm = ow.wrap(iterable);
assertThat(tm, instanceOf(TemplateCollectionModel.class));
TemplateCollectionModel iterableTM = (TemplateCollectionModel) tm;
@@ -661,7 +670,7 @@ public class DefaultObjectWrapperTest {
}
}
- assertTemplateOutput(OW300, iterable, listingFTL, "a, b, c");
+ assertTemplateOutput(OW, iterable, listingFTL, "a, b, c");
}
@Test
@@ -671,9 +680,27 @@ public class DefaultObjectWrapperTest {
InputSource is = new InputSource();
is.setCharacterStream(new StringReader("<doc><sub a='1' /></doc>"));
Document doc = db.parse(is);
- assertTrue(OW300.wrap(doc) instanceof TemplateNodeModel);
+ assertTrue(OW.wrap(doc) instanceof TemplateNodeModel);
}
-
+
+ @Test
+ public void testExposureLevel() throws Exception {
+ final DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
+
+ TemplateHashModel tm = (TemplateHashModel) ow.wrap(new TestBean());
+ assertNotNull(tm.get("hashCode"));
+ assertNotNull(tm.get("class"));
+ ow.setExposureLevel(DefaultObjectWrapper.EXPOSE_PROPERTIES_ONLY);
+ assertNull(tm.get("hashCode"));
+ assertNotNull(tm.get("class"));
+ ow.setExposureLevel(DefaultObjectWrapper.EXPOSE_NOTHING);
+ assertNull(tm.get("hashCode"));
+ assertNull(tm.get("class"));
+ ow.setExposureLevel(DefaultObjectWrapper.EXPOSE_ALL);
+ assertNotNull(tm.get("hashCode"));
+ assertNotNull(tm.get("class"));
+ }
+
private void assertSizeThroughAPIModel(int expectedSize, TemplateModel normalModel) throws TemplateModelException {
if (!(normalModel instanceof TemplateModelWithAPISupport)) {
fail();
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperWithShortedMethods.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperWithShortedMethods.java b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperWithShortedMethods.java
new file mode 100644
index 0000000..ec16f54
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperWithShortedMethods.java
@@ -0,0 +1,41 @@
+/*
+ * 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.Version;
+
+/**
+ * Used so that the order in which the methods are added to the introspection cache is deterministic.
+ */
+public abstract class DefaultObjectWrapperWithShortedMethods extends DefaultObjectWrapper {
+
+ public DefaultObjectWrapperWithShortedMethods(boolean desc) {
+ super(Configuration.VERSION_3_0_0);
+ setMethodSorter(new AlphabeticalMethodSorter(desc));
+ }
+
+ public DefaultObjectWrapperWithShortedMethods(Version incompatibleImprovements, boolean desc) {
+ super(incompatibleImprovements);
+ setMethodSorter(new AlphabeticalMethodSorter(desc));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperWithSortedMethods.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperWithSortedMethods.java b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperWithSortedMethods.java
new file mode 100644
index 0000000..a10247d
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperWithSortedMethods.java
@@ -0,0 +1,40 @@
+/*
+ * 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.Version;
+
+public class DefaultObjectWrapperWithSortedMethods extends DefaultObjectWrapper {
+
+ public DefaultObjectWrapperWithSortedMethods(boolean desc) {
+ this(Configuration.VERSION_3_0_0, desc);
+ }
+
+ public DefaultObjectWrapperWithSortedMethods(Version incompatibleImprovements, boolean desc) {
+ super(incompatibleImprovements);
+ setMethodSorter(this, desc);
+ }
+
+ static void setMethodSorter(DefaultObjectWrapper ow, boolean desc) {
+ ow.setMethodSorter(new AlphabeticalMethodSorter(desc));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java b/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
new file mode 100644
index 0000000..b608cda
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/EnumModelsTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.util.ArrayList;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EnumModelsTest {
+
+ @Test
+ public void modelCaching() throws Exception {
+ DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
+ TemplateHashModel enums = ow.getEnumModels();
+ TemplateHashModel e = (TemplateHashModel) enums.get(E.class.getName());
+ assertNotNull(e);
+ assertNotNull(e.get("A"));
+ assertNotNull(e.get("B"));
+ assertNull(e.get("C"));
+
+ try {
+ enums.get("no.such.ClassExists");
+ fail();
+ } catch (TemplateModelException ex) {
+ assertTrue(ex.getCause() instanceof ClassNotFoundException);
+ }
+
+ TemplateModel a = e.get("A");
+ assertTrue(a instanceof TemplateScalarModel);
+ assertTrue(a instanceof TemplateHashModel);
+ assertEquals(((TemplateScalarModel) a).getAsString(), "ts:A");
+ TemplateMethodModelEx nameMethod = (TemplateMethodModelEx) ((TemplateHashModel) a).get("name");
+ assertEquals(((TemplateScalarModel) nameMethod.exec(new ArrayList())).getAsString(), "A");
+
+ assertSame(e, enums.get(E.class.getName()));
+
+ ow.clearClassIntrospecitonCache();
+ TemplateHashModel eAfterClean = (TemplateHashModel) enums.get(E.class.getName());
+ assertNotSame(e, eAfterClean);
+ assertSame(eAfterClean, enums.get(E.class.getName()));
+ assertNotNull(eAfterClean.get("A"));
+ assertNotNull(eAfterClean.get("B"));
+ assertNull(eAfterClean.get("C"));
+ }
+
+ public enum E {
+ A, B;
+
+ @Override
+ public String toString() {
+ return "ts:" + super.toString();
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java b/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
new file mode 100644
index 0000000..3bd0363
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/ErrorMessagesTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.util.Collections;
+import java.util.Date;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
+import org.apache.freemarker.core.outputformat.impl.TemplateHTMLOutputModel;
+import org.junit.Test;
+
+public class ErrorMessagesTest {
+
+ @Test
+ public void getterMessage() throws TemplateModelException {
+ DefaultObjectWrapper ow = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ TemplateHashModel thm= (TemplateHashModel) ow.wrap(new TestBean());
+
+ try {
+ thm.get("foo");
+ } catch (TemplateModelException e) {
+ e.printStackTrace();
+ final String msg = e.getMessage();
+ assertThat(msg, containsString("\"foo\""));
+ assertThat(msg, containsString("existing sub-variable"));
+ }
+ assertNull(thm.get("bar"));
+ }
+
+ @Test
+ public void markupOutputParameter() throws Exception {
+ TemplateHTMLOutputModel html = HTMLOutputFormat.INSTANCE.fromMarkup("<p>a");
+
+ DefaultObjectWrapper ow = new DefaultObjectWrapperBuilder(Configuration.VERSION_3_0_0).build();
+ TemplateHashModel thm = (TemplateHashModel) ow.wrap(new TestBean());
+
+ {
+ TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("m1");
+ try {
+ m.exec(Collections.singletonList(html));
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), allOf(
+ containsString("String"), containsString("convert"), containsString("markup_output"),
+ containsString("Tip:"), containsString("?markup_string")));
+ }
+ }
+
+ {
+ TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("m2");
+ try {
+ m.exec(Collections.singletonList(html));
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), allOf(
+ containsString("Date"), containsString("convert"), containsString("markup_output"),
+ not(containsString("?markup_string"))));
+ }
+ }
+
+ for (String methodName : new String[] { "mOverloaded", "mOverloaded3" }) {
+ TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get(methodName);
+ try {
+ m.exec(Collections.singletonList(html));
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), allOf(
+ containsString("No compatible overloaded"),
+ containsString("String"), containsString("markup_output"),
+ containsString("Tip:"), containsString("?markup_string")));
+ }
+ }
+
+ {
+ TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("mOverloaded2");
+ try {
+ m.exec(Collections.singletonList(html));
+ fail();
+ } catch (TemplateModelException e) {
+ assertThat(e.getMessage(), allOf(
+ containsString("No compatible overloaded"),
+ containsString("Integer"), containsString("markup_output"),
+ not(containsString("?markup_string"))));
+ }
+ }
+
+ {
+ TemplateMethodModelEx m = (TemplateMethodModelEx) thm.get("mOverloaded4");
+ Object r = m.exec(Collections.singletonList(html));
+ if (r instanceof TemplateScalarModel) {
+ r = ((TemplateScalarModel) r).getAsString();
+ }
+ assertEquals("<p>a", r);
+ }
+ }
+
+ public static class TestBean {
+
+ public String getFoo() {
+ throw new RuntimeException("Dummy");
+ }
+
+ public void m1(String s) {
+ // nop
+ }
+
+ public void m2(Date s) {
+ // nop
+ }
+
+ public void mOverloaded(String s) {
+ // nop
+ }
+
+ public void mOverloaded(Date d) {
+ // nop
+ }
+
+ public void mOverloaded2(Integer n) {
+ // nop
+ }
+
+ public void mOverloaded2(Date d) {
+ // nop
+ }
+
+ public void mOverloaded3(String... s) {
+ // nop
+ }
+
+ public void mOverloaded3(Date d) {
+ // nop
+ }
+
+ public String mOverloaded4(String s) {
+ return s;
+ }
+
+ public String mOverloaded4(TemplateHTMLOutputModel s) throws TemplateModelException {
+ return s.getOutputFormat().getMarkupString(s);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java b/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
new file mode 100644
index 0000000..dfd554d
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/FineTuneMethodAppearanceTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FineTuneMethodAppearanceTest {
+
+ @Test
+ public void newWayOfConfiguring() throws TemplateModelException {
+ DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_3_0_0);
+ ow.setMethodAppearanceFineTuner(GetlessMethodsAsPropertyGettersRule.INSTANCE);
+ ow.setExposeFields(true);
+ checkIfProperlyWrapped(ow.wrap(new C()));
+ }
+
+ private void checkIfProperlyWrapped(TemplateModel tm) throws TemplateModelException {
+ TemplateHashModel thm = (TemplateHashModel) tm;
+ assertEquals("v1", ((TemplateScalarModel) thm.get("v1")).getAsString());
+ assertEquals("v2()", ((TemplateScalarModel) thm.get("v2")).getAsString());
+ assertEquals("getV3()", ((TemplateScalarModel) thm.get("v3")).getAsString());
+ assertTrue(thm.get("getV3") instanceof TemplateMethodModelEx);
+ }
+
+ static public class C {
+
+ public String v1 = "v1";
+
+ public String v2 = "v2";
+ public String v2() { return "v2()"; }
+
+ public String v3() { return "v3()"; }
+ public String getV3() { return "getV3()"; }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/GetlessMethodsAsPropertyGettersRule.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/GetlessMethodsAsPropertyGettersRule.java b/src/test/java/org/apache/freemarker/core/model/impl/GetlessMethodsAsPropertyGettersRule.java
new file mode 100644
index 0000000..d62d355
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/GetlessMethodsAsPropertyGettersRule.java
@@ -0,0 +1,67 @@
+/*
+ * 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.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+
+class GetlessMethodsAsPropertyGettersRule implements MethodAppearanceFineTuner, SingletonCustomizer {
+
+ static final GetlessMethodsAsPropertyGettersRule INSTANCE = new GetlessMethodsAsPropertyGettersRule();
+
+ // Can't be constructed from outside
+ private GetlessMethodsAsPropertyGettersRule() { }
+
+ @Override
+ public void process(
+ DecisionInput in, Decision out) {
+ legacyProcess(in.getContainingClass(), in.getMethod(), out);
+ }
+
+ /** This only exists as the tests need to call this through the deprecated method too. */
+ public void legacyProcess(
+ Class clazz, Method m, Decision decision) {
+ if (m.getDeclaringClass() != Object.class
+ && m.getReturnType() != void.class
+ && m.getParameterTypes().length == 0) {
+ String mName = m.getName();
+ if (!looksLikePropertyReadMethod(mName)) {
+ decision.setExposeMethodAs(null);
+ try {
+ decision.setExposeAsProperty(new PropertyDescriptor(
+ mName, clazz, mName, null));
+ } catch (IntrospectionException e) { // Won't happen...
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ private static boolean looksLikePropertyReadMethod(String name) {
+ final int verbEnd;
+ if (name.startsWith("get")) verbEnd = 3;
+ else if (name.startsWith("is")) verbEnd = 2;
+ else return false;
+
+ return name.length() == verbEnd || Character.isUpperCase(name.charAt(verbEnd));
+ }
+
+}
\ 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/IsApplicableTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/IsApplicableTest.java b/src/test/java/org/apache/freemarker/core/model/impl/IsApplicableTest.java
new file mode 100644
index 0000000..e7c473b
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/IsApplicableTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+@SuppressWarnings("boxing")
+public class IsApplicableTest extends TestCase {
+
+ public IsApplicableTest(String name) {
+ super(name);
+ }
+
+ public void testSingle() {
+ ArgumentTypes ats = new ArgumentTypes(new Object[] { new Object() });
+ assertApplicable(ats, Object.class);
+ assertNotApplicable(ats, String.class);
+ assertNotApplicable(ats, CharSequence.class);
+ assertNotApplicable(ats, Integer.class);
+ assertNotApplicable(ats, Integer.TYPE);
+
+ ats = new ArgumentTypes(new Object[] { "" });
+ assertApplicable(ats, Object.class);
+ assertApplicable(ats, CharSequence.class);
+ assertApplicable(ats, String.class);
+ assertNotApplicable(ats, Integer.class);
+ assertNotApplicable(ats, Integer.TYPE);
+
+ ats = new ArgumentTypes(new Object[] { 1 });
+ assertApplicable(ats, Object.class);
+ assertNotApplicable(ats, CharSequence.class);
+ assertNotApplicable(ats, String.class);
+ assertNotApplicable(ats, Short.class);
+ assertNotApplicable(ats, Short.TYPE);
+ assertApplicable(ats, Integer.class);
+ assertApplicable(ats, Integer.TYPE);
+ assertApplicable(ats, Float.class);
+ assertApplicable(ats, Float.TYPE);
+ assertApplicable(ats, Double.class);
+ assertApplicable(ats, Double.TYPE);
+ assertApplicable(ats, BigDecimal.class);
+ assertApplicable(ats, BigInteger.class);
+
+ ats = new ArgumentTypes(new Object[] { new OverloadedNumberUtil.IntegerOrByte(1, (byte) 1) });
+ assertApplicable(ats, Object.class);
+ assertNotApplicable(ats, CharSequence.class);
+ assertNotApplicable(ats, String.class);
+ assertApplicable(ats, Short.class);
+ assertApplicable(ats, Short.TYPE);
+ assertApplicable(ats, Integer.class);
+ assertApplicable(ats, Integer.TYPE);
+ assertApplicable(ats, Float.class);
+ assertApplicable(ats, Float.TYPE);
+ assertApplicable(ats, Double.class);
+ assertApplicable(ats, Double.TYPE);
+ assertApplicable(ats, BigDecimal.class);
+ assertApplicable(ats, BigInteger.class);
+
+ ats = new ArgumentTypes(new Object[] { 1.0f });
+ assertApplicable(ats, Object.class);
+ assertNotApplicable(ats, CharSequence.class);
+ assertNotApplicable(ats, String.class);
+ assertNotApplicable(ats, Integer.class);
+ assertNotApplicable(ats, Integer.TYPE);
+ assertApplicable(ats, Float.class);
+ assertApplicable(ats, Float.TYPE);
+ assertApplicable(ats, Double.class);
+ assertApplicable(ats, Double.TYPE);
+ assertApplicable(ats, BigDecimal.class);
+ assertNotApplicable(ats, BigInteger.class);
+
+ ats = new ArgumentTypes(new Object[] { null });
+ assertApplicable(ats, Object.class);
+ assertApplicable(ats, String.class);
+ assertApplicable(ats, Integer.class);
+ assertNotApplicable(ats, Integer.TYPE);
+ assertNotApplicable(ats, Boolean.TYPE);
+ assertNotApplicable(ats, Object.class, Object.class);
+ assertNotApplicable(ats);
+ }
+
+ public void testMulti() {
+ ArgumentTypes ats = new ArgumentTypes(new Object[] { new Object(), "", 1, true });
+ assertApplicable(ats, Object.class, Object.class, Object.class, Object.class);
+ assertApplicable(ats, Object.class, String.class, Number.class, Boolean.class);
+ assertApplicable(ats, Object.class, CharSequence.class, Integer.class, Serializable.class);
+ assertApplicable(ats, Object.class, Comparable.class, Integer.TYPE, Serializable.class);
+ assertNotApplicable(ats, Object.class, String.class, Number.class, Number.class);
+ assertNotApplicable(ats, Object.class, StringBuilder.class, Number.class, Boolean.class);
+ assertNotApplicable(ats, int.class, Object.class, Object.class, Object.class);
+ assertNotApplicable(ats, Object.class, Object.class, Object.class);
+ assertNotApplicable(ats, Object.class, Object.class, Object.class, Object.class, Object.class);
+ }
+
+ public void testNoParam() {
+ ArgumentTypes ats = new ArgumentTypes(new Object[] { });
+ assertApplicable(ats);
+ assertNotApplicable(ats, Object.class);
+ }
+
+ public void testVarags() {
+ Object[][] argLists = new Object[][] {
+ new Object[] { "", 1, 2, 3 },
+ new Object[] { "", 1, (byte) 2, 3 },
+ new Object[] { "", 1},
+ new Object[] { "" },
+ };
+ for (Object[] args : argLists) {
+ ArgumentTypes ats = new ArgumentTypes(args);
+ assertApplicable(ats, true, String.class, int[].class);
+ assertApplicable(ats, true, String.class, Integer[].class);
+ assertApplicable(ats, true, Object.class, Comparable[].class);
+ assertApplicable(ats, true, Object.class, Object[].class);
+ assertNotApplicable(ats, true, StringBuilder.class, int[].class);
+ if (args.length > 1) {
+ assertNotApplicable(ats, true, String.class, String[].class);
+ } else {
+ assertApplicable(ats, true, String.class, String[].class);
+ }
+ }
+ }
+
+ private void assertNotApplicable(ArgumentTypes ats, Class... paramTypes) {
+ assertNotApplicable(ats, false, paramTypes);
+ }
+
+ private void assertNotApplicable(ArgumentTypes ats, boolean varargs, Class... paramTypes) {
+ List tested = new ArrayList();
+ tested.add(new ReflectionCallableMemberDescriptor((Method) null, paramTypes));
+ if (ats.getApplicables(tested, varargs).size() != 0) {
+ fail("Parameter types were applicable");
+ }
+ }
+
+ private void assertApplicable(ArgumentTypes ats, Class<?>... paramTypes) {
+ assertApplicable(ats, false, paramTypes);
+ }
+
+ private void assertApplicable(ArgumentTypes ats, boolean varargs, Class<?>... paramTypes) {
+ List tested = new ArrayList();
+ tested.add(new ReflectionCallableMemberDescriptor((Method) null, paramTypes));
+ if (ats.getApplicables(tested, varargs).size() != 1) {
+ fail("Parameter types weren't applicable");
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/test/java/org/apache/freemarker/core/model/impl/IsMoreSpecificParameterTypeTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/model/impl/IsMoreSpecificParameterTypeTest.java b/src/test/java/org/apache/freemarker/core/model/impl/IsMoreSpecificParameterTypeTest.java
new file mode 100644
index 0000000..fc45156
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/model/impl/IsMoreSpecificParameterTypeTest.java
@@ -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.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class IsMoreSpecificParameterTypeTest extends TestCase {
+
+ public IsMoreSpecificParameterTypeTest(String name) {
+ super(name);
+ }
+
+ public void testFixed() {
+ assertEquals(1, _MethodUtil.isMoreOrSameSpecificParameterType(String.class, String.class, true, 0));
+ assertEquals(1, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, int.class, true, 0));
+
+ assertEquals(2, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Integer.class, true, 0));
+ assertEquals(2, _MethodUtil.isMoreOrSameSpecificParameterType(boolean.class, Boolean.class, true, 0));
+
+ assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, long.class, true, 0));
+ assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, double.class, true, 0));
+ assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Long.class, true, 0));
+ assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Double.class, true, 0));
+ assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Long.class, true, 0));
+ assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Double.class, true, 0));
+
+ assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(HashMap.class, Map.class, true, 0));
+ assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(String.class, CharSequence.class, true, 0));
+ assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Number.class, true, 0));
+ assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Number.class, true, 0));
+
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Map.class, String.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, int.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Boolean.class, boolean.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, boolean.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Boolean.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, String.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, BigDecimal.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Long.class, Integer.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(long.class, Integer.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Long.class, int.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, BigDecimal.class, true, 0));
+ }
+
+ public void testBuggy() {
+ assertEquals(1, _MethodUtil.isMoreOrSameSpecificParameterType(String.class, String.class, false, 0));
+ assertEquals(1, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, int.class, false, 0));
+
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Integer.class, false, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(boolean.class, Boolean.class, false, 0));
+
+ assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, long.class, false, 0));
+ assertEquals(3, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, double.class, false, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Long.class, false, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Double.class, false, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Long.class, false, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Double.class, false, 0));
+
+ assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(HashMap.class, Map.class, false, 0));
+ assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(String.class, CharSequence.class, false, 0));
+ assertEquals(4, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, Number.class, false, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Number.class, false, 0));
+
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Map.class, String.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, int.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Boolean.class, boolean.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, boolean.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, Boolean.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, String.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(int.class, BigDecimal.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Long.class, Integer.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(long.class, Integer.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Long.class, int.class, true, 0));
+ assertEquals(0, _MethodUtil.isMoreOrSameSpecificParameterType(Integer.class, BigDecimal.class, true, 0));
+ }
+
+}
[11/20] incubator-freemarker git commit: Removed BeansWrapper,
merging it into DefaultObjectWrapper (the o.a.f.core.model.impl.beans
packageis gone now). It works,
but there's a lot of unused classes and logic now,
which will have to be removed.
Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/EnumerationModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/EnumerationModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/EnumerationModel.java
deleted file mode 100644
index e90aab9..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/EnumerationModel.java
+++ /dev/null
@@ -1,108 +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.util.Enumeration;
-import java.util.NoSuchElementException;
-
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-
-/**
- * <p>A class that adds {@link TemplateModelIterator} functionality to the
- * {@link Enumeration} interface implementers.
- * </p> <p>Using the model as a collection model is NOT thread-safe, as
- * enumerations are inherently not thread-safe.
- * Further, you can iterate over it only once. Attempts to call the
- * {@link #iterator()} method after it was already driven to the end once will
- * throw an exception.</p>
- */
-
-public class EnumerationModel
-extends
- BeanModel
-implements
- TemplateModelIterator,
- TemplateCollectionModel {
- private boolean accessed = false;
-
- /**
- * Creates a new model that wraps the specified enumeration object.
- * @param enumeration the enumeration object to wrap into a model.
- * @param wrapper the {@link BeansWrapper} associated with this model.
- * Every model has to have an associated {@link BeansWrapper} instance. The
- * model gains many attributes from its wrapper, including the caching
- * behavior, method exposure level, method-over-item shadowing policy etc.
- */
- public EnumerationModel(Enumeration enumeration, BeansWrapper wrapper) {
- super(enumeration, wrapper);
- }
-
- /**
- * This allows the enumeration to be used in a <tt><#list></tt> block.
- * @return "this"
- */
- @Override
- public TemplateModelIterator iterator() throws TemplateModelException {
- synchronized (this) {
- if (accessed) {
- throw new TemplateModelException(
- "This collection is stateful and can not be iterated over the" +
- " second time.");
- }
- accessed = true;
- }
- return this;
- }
-
- /**
- * Calls underlying {@link Enumeration#nextElement()}.
- */
- @Override
- public boolean hasNext() {
- return ((Enumeration) object).hasMoreElements();
- }
-
-
- /**
- * Calls underlying {@link Enumeration#nextElement()} and wraps the result.
- */
- @Override
- public TemplateModel next()
- throws TemplateModelException {
- try {
- return wrap(((Enumeration) object).nextElement());
- } catch (NoSuchElementException e) {
- throw new TemplateModelException(
- "No more elements in the enumeration.");
- }
- }
-
- /**
- * Returns {@link Enumeration#hasMoreElements()}. Therefore, an
- * enumeration that has no more element evaluates to false, and an
- * enumeration that has further elements evaluates to true.
- */
- public boolean getAsBoolean() {
- return hasNext();
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/HashAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/HashAdapter.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/HashAdapter.java
deleted file mode 100644
index 0fcc58f..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/HashAdapter.java
+++ /dev/null
@@ -1,180 +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.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelAdapter;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.util.UndeclaredThrowableException;
-
-/**
- */
-public class HashAdapter extends AbstractMap implements TemplateModelAdapter {
- private final BeansWrapper wrapper;
- private final TemplateHashModel model;
- private Set entrySet;
-
- HashAdapter(TemplateHashModel model, BeansWrapper wrapper) {
- this.model = model;
- this.wrapper = wrapper;
- }
-
- @Override
- public TemplateModel getTemplateModel() {
- return model;
- }
-
- @Override
- public boolean isEmpty() {
- try {
- return model.isEmpty();
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public Object get(Object key) {
- try {
- return wrapper.unwrap(model.get(String.valueOf(key)));
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public boolean containsKey(Object key) {
- // A quick check that doesn't require TemplateHashModelEx
- if (get(key) != null) {
- return true;
- }
- return super.containsKey(key);
- }
-
- @Override
- public Set entrySet() {
- if (entrySet != null) {
- return entrySet;
- }
- return entrySet = new AbstractSet() {
- @Override
- public Iterator iterator() {
- final TemplateModelIterator i;
- try {
- i = getModelEx().keys().iterator();
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- return new Iterator() {
- @Override
- public boolean hasNext() {
- try {
- return i.hasNext();
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public Object next() {
- final Object key;
- try {
- key = wrapper.unwrap(i.next());
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- return new Map.Entry() {
- @Override
- public Object getKey() {
- return key;
- }
-
- @Override
- public Object getValue() {
- return get(key);
- }
-
- @Override
- public Object setValue(Object value) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry e = (Map.Entry) o;
- Object k1 = getKey();
- Object k2 = e.getKey();
- if (k1 == k2 || (k1 != null && k1.equals(k2))) {
- Object v1 = getValue();
- Object v2 = e.getValue();
- if (v1 == v2 || (v1 != null && v1.equals(v2)))
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- Object value = getValue();
- return (key == null ? 0 : key.hashCode()) ^
- (value == null ? 0 : value.hashCode());
- }
- };
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- }
-
- @Override
- public int size() {
- try {
- return getModelEx().size();
- } catch (TemplateModelException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
- };
- }
-
- private TemplateHashModelEx getModelEx() {
- if (model instanceof TemplateHashModelEx) {
- return ((TemplateHashModelEx) model);
- }
- throw new UnsupportedOperationException(
- "Operation supported only on TemplateHashModelEx. " +
- model.getClass().getName() + " does not implement it though.");
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/InvalidPropertyException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/InvalidPropertyException.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/InvalidPropertyException.java
deleted file mode 100755
index 5187b15..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/InvalidPropertyException.java
+++ /dev/null
@@ -1,34 +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 org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * An exception thrown when there is an attempt to access
- * an invalid bean property when we are in a "strict bean" mode
- */
-
-public class InvalidPropertyException extends TemplateModelException {
-
- public InvalidPropertyException(String description) {
- super(description);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/IteratorModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/IteratorModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/IteratorModel.java
deleted file mode 100644
index 767c15e..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/IteratorModel.java
+++ /dev/null
@@ -1,112 +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.util.Iterator;
-import java.util.NoSuchElementException;
-
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-
-/**
- * <p>A class that adds {@link TemplateModelIterator} functionality to the
- * {@link Iterator} interface implementers.
- * </p>
- * <p>It differs from the {@link org.apache.freemarker.core.model.impl.SimpleCollection} in that
- * it inherits from {@link BeanModel}, and therefore you can call methods on
- * it directly, even to the effect of calling <tt>iterator.remove()</tt> in
- * the template.</p> <p>Using the model as a collection model is NOT
- * thread-safe, as iterators are inherently not thread-safe.
- * Further, you can iterate over it only once. Attempts to call the
- * {@link #iterator()} method after it was already driven to the end once will
- * throw an exception.</p>
- */
-
-public class IteratorModel
-extends
- BeanModel
-implements
- TemplateModelIterator,
- TemplateCollectionModel {
- private boolean accessed = false;
-
- /**
- * Creates a new model that wraps the specified iterator object.
- * @param iterator the iterator object to wrap into a model.
- * @param wrapper the {@link BeansWrapper} associated with this model.
- * Every model has to have an associated {@link BeansWrapper} instance. The
- * model gains many attributes from its wrapper, including the caching
- * behavior, method exposure level, method-over-item shadowing policy etc.
- */
- public IteratorModel(Iterator iterator, BeansWrapper wrapper) {
- super(iterator, wrapper);
- }
-
- /**
- * This allows the iterator to be used in a <tt><#list></tt> block.
- * @return "this"
- */
- @Override
- public TemplateModelIterator iterator() throws TemplateModelException {
- synchronized (this) {
- if (accessed) {
- throw new TemplateModelException(
- "This collection is stateful and can not be iterated over the" +
- " second time.");
- }
- accessed = true;
- }
- return this;
- }
-
- /**
- * Calls underlying {@link Iterator#hasNext()}.
- */
- @Override
- public boolean hasNext() {
- return ((Iterator) object).hasNext();
- }
-
-
- /**
- * Calls underlying {@link Iterator#next()} and wraps the result.
- */
- @Override
- public TemplateModel next()
- throws TemplateModelException {
- try {
- return wrap(((Iterator) object).next());
- } catch (NoSuchElementException e) {
- throw new TemplateModelException(
- "No more elements in the iterator.", e);
- }
- }
-
- /**
- * Returns {@link Iterator#hasNext()}. Therefore, an
- * iterator that has no more element evaluates to false, and an
- * iterator that has further elements evaluates to true.
- */
- public boolean getAsBoolean() {
- return hasNext();
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/JRebelClassChangeNotifier.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/JRebelClassChangeNotifier.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/JRebelClassChangeNotifier.java
deleted file mode 100644
index 956a6c4..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/JRebelClassChangeNotifier.java
+++ /dev/null
@@ -1,58 +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.ref.WeakReference;
-
-import org.zeroturnaround.javarebel.ClassEventListener;
-import org.zeroturnaround.javarebel.ReloaderFactory;
-
-class JRebelClassChangeNotifier implements ClassChangeNotifier {
-
- static void testAvailability() {
- ReloaderFactory.getInstance();
- }
-
- @Override
- public void subscribe(ClassIntrospector classIntrospector) {
- ReloaderFactory.getInstance().addClassReloadListener(
- new ClassIntrospectorCacheInvalidator(classIntrospector));
- }
-
- private static class ClassIntrospectorCacheInvalidator
- implements ClassEventListener {
- private final WeakReference ref;
-
- ClassIntrospectorCacheInvalidator(ClassIntrospector w) {
- ref = new WeakReference(w);
- }
-
- @Override
- public void onClassEvent(int eventType, Class pClass) {
- ClassIntrospector ci = (ClassIntrospector) ref.get();
- if (ci == null) {
- ReloaderFactory.getInstance().removeClassReloadListener(this);
- } else if (eventType == ClassEventListener.EVENT_RELOADED) {
- ci.remove(pClass);
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/MapModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/MapModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/MapModel.java
deleted file mode 100644
index 0af8749..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/MapModel.java
+++ /dev/null
@@ -1,120 +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.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * <p>A special case of {@link BeanModel} that adds implementation
- * for {@link TemplateMethodModelEx} on map objects that is a shortcut for the
- * <tt>Map.get()</tt> method. Note that if the passed argument itself is a
- * reflection-wrapper model, then the map lookup will be performed using the
- * wrapped object as the key. Note that you can call <tt>get()</tt> using the
- * <tt>map.key</tt> syntax inherited from {@link BeanModel} as well,
- * however in that case the key is always a string.</p>
- * <p>The class itself does not implement the {@link org.apache.freemarker.core.model.TemplateCollectionModel}.
- * You can, however use <tt>map.entrySet()</tt>, <tt>map.keySet()</tt>, or
- * <tt>map.values()</tt> to obtain {@link org.apache.freemarker.core.model.TemplateCollectionModel} instances for
- * various aspects of the map.</p>
- */
-public class MapModel
-extends
- StringModel
-implements
- TemplateMethodModelEx {
- static final ModelFactory FACTORY =
- new ModelFactory()
- {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new MapModel((Map) object, (BeansWrapper) wrapper);
- }
- };
-
- /**
- * Creates a new model that wraps the specified map object.
- * @param map the map object to wrap into a model.
- * @param wrapper the {@link BeansWrapper} associated with this model.
- * Every model has to have an associated {@link BeansWrapper} instance. The
- * model gains many attributes from its wrapper, including the caching
- * behavior, method exposure level, method-over-item shadowing policy etc.
- */
- public MapModel(Map map, BeansWrapper wrapper) {
- super(map, wrapper);
- }
-
- /**
- * The first argument is used as a key to call the map's <tt>get</tt> method.
- */
- @Override
- public Object exec(List arguments)
- throws TemplateModelException {
- Object key = unwrap((TemplateModel) arguments.get(0));
- return wrap(((Map) object).get(key));
- }
-
- /**
- * Overridden to invoke the generic get method by casting to Map instead of
- * through reflection - should yield better performance.
- */
- @Override
- protected TemplateModel invokeGenericGet(Map keyMap, Class clazz, String key)
- throws TemplateModelException {
- Map map = (Map) object;
- Object val = map.get(key);
- if (val == null) {
- if (key.length() == 1) {
- // just check for Character key if this is a single-character string
- Character charKey = Character.valueOf(key.charAt(0));
- val = map.get(charKey);
- if (val == null && !(map.containsKey(key) || map.containsKey(charKey))) {
- return UNKNOWN;
- }
- } else if (!map.containsKey(key)) {
- return UNKNOWN;
- }
- }
- return wrap(val);
- }
-
- @Override
- public boolean isEmpty() {
- return ((Map) object).isEmpty() && super.isEmpty();
- }
-
- @Override
- public int size() {
- return keySet().size();
- }
-
- @Override
- protected Set keySet() {
- Set set = super.keySet();
- set.addAll(((Map) object).keySet());
- return set;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/MaybeEmptyCallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/MaybeEmptyCallableMemberDescriptor.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/MaybeEmptyCallableMemberDescriptor.java
deleted file mode 100644
index 03791ab..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/MaybeEmptyCallableMemberDescriptor.java
+++ /dev/null
@@ -1,25 +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;
-
-/**
- * Superclass of the {@link EmptyCallableMemberDescriptor} and {@link CallableMemberDescriptor} "case classes".
- */
-abstract class MaybeEmptyCallableMemberDescriptor { }
\ 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/MaybeEmptyMemberAndArguments.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/MaybeEmptyMemberAndArguments.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/MaybeEmptyMemberAndArguments.java
deleted file mode 100644
index c278243..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/MaybeEmptyMemberAndArguments.java
+++ /dev/null
@@ -1,22 +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;
-
-abstract class MaybeEmptyMemberAndArguments { }
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/MemberAndArguments.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/MemberAndArguments.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/MemberAndArguments.java
deleted file mode 100644
index b22a63e..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/MemberAndArguments.java
+++ /dev/null
@@ -1,64 +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.InvocationTargetException;
-
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- */
-class MemberAndArguments extends MaybeEmptyMemberAndArguments {
-
- private final CallableMemberDescriptor callableMemberDesc;
- private final Object[] args;
-
- /**
- * @param args The already unwrapped arguments
- */
- MemberAndArguments(CallableMemberDescriptor callableMemberDesc, Object[] args) {
- this.callableMemberDesc = callableMemberDesc;
- this.args = args;
- }
-
- /**
- * The already unwrapped arguments.
- */
- Object[] getArgs() {
- return args;
- }
-
- TemplateModel invokeMethod(BeansWrapper bw, Object obj)
- throws TemplateModelException, InvocationTargetException, IllegalAccessException {
- return callableMemberDesc.invokeMethod(bw, obj, args);
- }
-
- Object invokeConstructor(BeansWrapper bw)
- throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException,
- TemplateModelException {
- return callableMemberDesc.invokeConstructor(bw, args);
- }
-
- CallableMemberDescriptor getCallableMemberDescriptor() {
- return callableMemberDesc;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/MethodAppearanceFineTuner.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/MethodAppearanceFineTuner.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/MethodAppearanceFineTuner.java
deleted file mode 100644
index ea72fb2..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/MethodAppearanceFineTuner.java
+++ /dev/null
@@ -1,94 +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.beans.IndexedPropertyDescriptor;
-import java.beans.PropertyDescriptor;
-
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecision;
-import org.apache.freemarker.core.model.impl.beans.BeansWrapper.MethodAppearanceDecisionInput;
-
-/**
- * Used for customizing how the methods are visible from templates, via
- * {@link BeansWrapper#setMethodAppearanceFineTuner(MethodAppearanceFineTuner)}.
- * The object that implements this should also implement {@link SingletonCustomizer} whenever possible.
- *
- * @since 2.3.21
- */
-public interface MethodAppearanceFineTuner {
-
- /**
- * Implement this to tweak certain aspects of how methods appear in the
- * data-model. {@link BeansWrapper} will pass in all Java methods here that
- * it intends to expose in the data-model as methods (so you can do
- * <tt>obj.foo()</tt> in the template).
- * With this method you can do the following tweaks:
- * <ul>
- * <li>Hide a method that would be otherwise shown by calling
- * {@link MethodAppearanceDecision#setExposeMethodAs(String)}
- * with <tt>null</tt> parameter. Note that you can't un-hide methods
- * that are not public or are considered to by unsafe
- * (like {@link Object#wait()}) because
- * {@link #process} is not called for those.</li>
- * <li>Show the method with a different name in the data-model than its
- * real name by calling
- * {@link MethodAppearanceDecision#setExposeMethodAs(String)}
- * with non-<tt>null</tt> parameter.
- * <li>Create a fake JavaBean property for this method by calling
- * {@link MethodAppearanceDecision#setExposeAsProperty(PropertyDescriptor)}.
- * For example, if you have <tt>int size()</tt> in a class, but you
- * want it to be accessed from the templates as <tt>obj.size</tt>,
- * rather than as <tt>obj.size()</tt>, you can do that with this.
- * The default is {@code null}, which means that no fake property is
- * created for the method. You need not and shouldn't set this
- * to non-<tt>null</tt> for the getter methods of real JavaBean
- * properties, as those are automatically shown as properties anyway.
- * The property name in the {@link PropertyDescriptor} can be anything,
- * but the method (or methods) in it must belong to the class that
- * is given as the <tt>clazz</tt> parameter or it must be inherited from
- * that class, or else whatever errors can occur later.
- * {@link IndexedPropertyDescriptor}-s are supported.
- * If a real JavaBean property of the same name exists, it won't be
- * replaced by the fake one. Also if a fake property of the same name
- * was assigned earlier, it won't be replaced.
- * <li>Prevent the method to hide a JavaBean property (fake or real) of
- * the same name by calling
- * {@link MethodAppearanceDecision#setMethodShadowsProperty(boolean)}
- * with <tt>false</tt>. The default is <tt>true</tt>, so if you have
- * both a property and a method called "foo", then in the template
- * <tt>myObject.foo</tt> will return the method itself instead
- * of the property value, which is often undesirable.
- * </ul>
- *
- * <p>Note that you can expose a Java method both as a method and as a
- * JavaBean property on the same time, however you have to chose different
- * names for them to prevent shadowing.
- *
- * @param in Describes the method about which the decision will have to be made.
- *
- * @param out Stores how the method will be exposed in the
- * data-model after {@link #process} returns.
- * This is initialized so that it reflects the default
- * behavior of {@link BeansWrapper}, so you don't have to do anything with this
- * when you don't want to change the default behavior.
- */
- void process(MethodAppearanceDecisionInput in, MethodAppearanceDecision out);
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/MethodSorter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/MethodSorter.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/MethodSorter.java
deleted file mode 100644
index d812910..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/MethodSorter.java
+++ /dev/null
@@ -1,32 +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.beans.MethodDescriptor;
-
-/**
- * Used for JUnit testing method-order dependence bugs via
- * {@link BeansWrapperBuilder#setMethodSorter(MethodSorter)}.
- */
-interface MethodSorter {
-
- MethodDescriptor[] sortMethodDescriptors(MethodDescriptor[] methodDescriptors);
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/ModelCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ModelCache.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ModelCache.java
deleted file mode 100644
index 4c5e415..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ModelCache.java
+++ /dev/null
@@ -1,143 +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.ref.ReferenceQueue;
-import java.lang.ref.SoftReference;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelAdapter;
-
-/**
- * Internally used by various wrapper implementations to implement model
- * caching.
- */
-public abstract class ModelCache {
- private boolean useCache = false;
- private Map<Object, ModelReference> modelCache = null;
- private ReferenceQueue<TemplateModel> refQueue = null;
-
- protected ModelCache() {
- }
-
- /**
- * Sets whether this wrapper caches model instances. Default is false.
- * When set to true, calling {@link #getInstance(Object)}
- * multiple times for the same object will return the same model.
- */
- public synchronized void setUseCache(boolean useCache) {
- this.useCache = useCache;
- if (useCache) {
- modelCache = new IdentityHashMap<>();
- refQueue = new ReferenceQueue<>();
- } else {
- modelCache = null;
- refQueue = null;
- }
- }
-
- /**
- * @since 2.3.21
- */
- public synchronized boolean getUseCache() {
- return useCache;
- }
-
- public TemplateModel getInstance(Object object) {
- if (object instanceof TemplateModel) {
- return (TemplateModel) object;
- }
- if (object instanceof TemplateModelAdapter) {
- return ((TemplateModelAdapter) object).getTemplateModel();
- }
- if (useCache && isCacheable(object)) {
- TemplateModel model = lookup(object);
- if (model == null) {
- model = create(object);
- register(model, object);
- }
- return model;
- } else {
- return create(object);
- }
- }
-
- protected abstract TemplateModel create(Object object);
- protected abstract boolean isCacheable(Object object);
-
- public void clearCache() {
- if (modelCache != null) {
- synchronized (modelCache) {
- modelCache.clear();
- }
- }
- }
-
- private TemplateModel lookup(Object object) {
- ModelReference ref = null;
- // NOTE: we're doing minimal synchronizations -- which can lead to
- // duplicate wrapper creation. However, this has no harmful side-effects and
- // is a lesser performance hit.
- synchronized (modelCache) {
- ref = modelCache.get(object);
- }
-
- if (ref != null)
- return ref.getModel();
-
- return null;
- }
-
- private void register(TemplateModel model, Object object) {
- synchronized (modelCache) {
- // Remove cleared references
- for (; ; ) {
- ModelReference queuedRef = (ModelReference) refQueue.poll();
- if (queuedRef == null) {
- break;
- }
- modelCache.remove(queuedRef.object);
- }
- // Register new reference
- modelCache.put(object, new ModelReference(model, object, refQueue));
- }
- }
-
- /**
- * A special soft reference that is registered in the modelCache.
- * When it gets cleared (that is, the model became unreachable)
- * it will remove itself from the model cache.
- */
- private static final class ModelReference extends SoftReference<TemplateModel> {
- Object object;
-
- ModelReference(TemplateModel ref, Object object, ReferenceQueue<TemplateModel> refQueue) {
- super(ref, refQueue);
- this.object = object;
- }
-
- TemplateModel getModel() {
- return get();
- }
- }
-
-}
\ 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/ModelFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/ModelFactory.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/ModelFactory.java
deleted file mode 100644
index a09d31b..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/ModelFactory.java
+++ /dev/null
@@ -1,34 +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 org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateModel;
-
-/**
- * Interface used to create various wrapper models in the {@link ModelCache}.
- */
-public interface ModelFactory {
- /**
- * Create a wrapping model for the specified object that belongs to
- * the specified wrapper.
- */
- TemplateModel create(Object object, ObjectWrapper wrapper);
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/NonPrimitiveArrayBackedReadOnlyList.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/NonPrimitiveArrayBackedReadOnlyList.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/NonPrimitiveArrayBackedReadOnlyList.java
deleted file mode 100644
index f2fc97d..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/NonPrimitiveArrayBackedReadOnlyList.java
+++ /dev/null
@@ -1,42 +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.util.AbstractList;
-
-class NonPrimitiveArrayBackedReadOnlyList extends AbstractList {
-
- private final Object[] array;
-
- NonPrimitiveArrayBackedReadOnlyList(Object[] array) {
- this.array = array;
- }
-
- @Override
- public Object get(int index) {
- return array[index];
- }
-
- @Override
- public int size() {
- return array.length;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/NumberModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/NumberModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/NumberModel.java
deleted file mode 100644
index 1d0013e..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/NumberModel.java
+++ /dev/null
@@ -1,60 +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 org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateNumberModel;
-
-/**
- * Wraps arbitrary subclass of {@link java.lang.Number} into a reflective model.
- * Beside acting as a {@link TemplateNumberModel}, you can call all Java methods on
- * these objects as well.
- */
-public class NumberModel
-extends
- BeanModel
-implements
- TemplateNumberModel {
- static final ModelFactory FACTORY =
- new ModelFactory()
- {
- @Override
- public TemplateModel create(Object object, ObjectWrapper wrapper) {
- return new NumberModel((Number) object, (BeansWrapper) wrapper);
- }
- };
- /**
- * Creates a new model that wraps the specified number object.
- * @param number the number object to wrap into a model.
- * @param wrapper the {@link BeansWrapper} associated with this model.
- * Every model has to have an associated {@link BeansWrapper} instance. The
- * model gains many attributes from its wrapper, including the caching
- * behavior, method exposure level, method-over-item shadowing policy etc.
- */
- public NumberModel(Number number, BeansWrapper wrapper) {
- super(number, wrapper);
- }
-
- @Override
- public Number getAsNumber() {
- return (Number) object;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedFixArgsMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedFixArgsMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedFixArgsMethods.java
deleted file mode 100644
index a1de28b..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedFixArgsMethods.java
+++ /dev/null
@@ -1,99 +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.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-
-/**
- * Stores the non-varargs methods for a {@link OverloadedMethods} object.
- */
-class OverloadedFixArgsMethods extends OverloadedMethodsSubset {
-
- OverloadedFixArgsMethods() {
- super();
- }
-
- @Override
- Class[] preprocessParameterTypes(CallableMemberDescriptor memberDesc) {
- return memberDesc.getParamTypes();
- }
-
- @Override
- void afterWideningUnwrappingHints(Class[] paramTypes, int[] paramNumericalTypes) {
- // Do nothing
- }
-
- @Override
- MaybeEmptyMemberAndArguments getMemberAndArguments(List tmArgs, BeansWrapper unwrapper)
- throws TemplateModelException {
- if (tmArgs == null) {
- // null is treated as empty args
- tmArgs = Collections.EMPTY_LIST;
- }
- final int argCount = tmArgs.size();
- final Class[][] unwrappingHintsByParamCount = getUnwrappingHintsByParamCount();
- if (unwrappingHintsByParamCount.length <= argCount) {
- return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS;
- }
- Class[] unwarppingHints = unwrappingHintsByParamCount[argCount];
- if (unwarppingHints == null) {
- return EmptyMemberAndArguments.WRONG_NUMBER_OF_ARGUMENTS;
- }
-
- Object[] pojoArgs = new Object[argCount];
-
- int[] typeFlags = getTypeFlags(argCount);
- if (typeFlags == ALL_ZEROS_ARRAY) {
- typeFlags = null;
- }
-
- Iterator it = tmArgs.iterator();
- for (int i = 0; i < argCount; ++i) {
- Object pojo = unwrapper.tryUnwrapTo(
- (TemplateModel) it.next(),
- unwarppingHints[i],
- typeFlags != null ? typeFlags[i] : 0);
- if (pojo == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
- return EmptyMemberAndArguments.noCompatibleOverload(i + 1);
- }
- pojoArgs[i] = pojo;
- }
-
- MaybeEmptyCallableMemberDescriptor maybeEmtpyMemberDesc = getMemberDescriptorForArgs(pojoArgs, false);
- if (maybeEmtpyMemberDesc instanceof CallableMemberDescriptor) {
- CallableMemberDescriptor memberDesc = (CallableMemberDescriptor) maybeEmtpyMemberDesc;
- if (typeFlags != null) {
- // Note that overloaded method selection has already accounted for overflow errors when the method
- // was selected. So this forced conversion shouldn't cause such corruption. Except, conversion from
- // BigDecimal is allowed to overflow for backward-compatibility.
- forceNumberArgumentsToParameterTypes(pojoArgs, memberDesc.getParamTypes(), typeFlags);
- }
- return new MemberAndArguments(memberDesc, pojoArgs);
- } else {
- return EmptyMemberAndArguments.from((EmptyCallableMemberDescriptor) maybeEmtpyMemberDesc, pojoArgs);
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethods.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethods.java
deleted file mode 100644
index f53596b..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethods.java
+++ /dev/null
@@ -1,271 +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.Method;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.freemarker.core._DelayedConversionToString;
-import org.apache.freemarker.core._ErrorDescriptionBuilder;
-import org.apache.freemarker.core._TemplateModelException;
-import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.util.FTLUtil;
-import org.apache.freemarker.core.util._ClassUtil;
-
-/**
- * Used instead of {@link java.lang.reflect.Method} or {@link java.lang.reflect.Constructor} for overloaded methods and
- * constructors.
- *
- * <p>After the initialization with the {@link #addMethod(Method)} and {@link #addConstructor(Constructor)} calls are
- * done, the instance must be thread-safe. Before that, it's the responsibility of the caller of those methods to
- * ensure that the object is properly publishing to other threads.
- */
-final class OverloadedMethods {
-
- private final OverloadedMethodsSubset fixArgMethods;
- private OverloadedMethodsSubset varargMethods;
-
- OverloadedMethods() {
- fixArgMethods = new OverloadedFixArgsMethods();
- }
-
- void addMethod(Method method) {
- final Class[] paramTypes = method.getParameterTypes();
- addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(method, paramTypes));
- }
-
- void addConstructor(Constructor constr) {
- final Class[] paramTypes = constr.getParameterTypes();
- addCallableMemberDescriptor(new ReflectionCallableMemberDescriptor(constr, paramTypes));
- }
-
- private void addCallableMemberDescriptor(ReflectionCallableMemberDescriptor memberDesc) {
- // Note: "varargs" methods are always callable as oms args, with a sequence (array) as the last parameter.
- fixArgMethods.addCallableMemberDescriptor(memberDesc);
- if (memberDesc.isVarargs()) {
- if (varargMethods == null) {
- varargMethods = new OverloadedVarArgsMethods();
- }
- varargMethods.addCallableMemberDescriptor(memberDesc);
- }
- }
-
- MemberAndArguments getMemberAndArguments(List/*<TemplateModel>*/ tmArgs, BeansWrapper unwrapper)
- throws TemplateModelException {
- // Try to find a oms args match:
- MaybeEmptyMemberAndArguments fixArgsRes = fixArgMethods.getMemberAndArguments(tmArgs, unwrapper);
- if (fixArgsRes instanceof MemberAndArguments) {
- return (MemberAndArguments) fixArgsRes;
- }
-
- // Try to find a varargs match:
- MaybeEmptyMemberAndArguments varargsRes;
- if (varargMethods != null) {
- varargsRes = varargMethods.getMemberAndArguments(tmArgs, unwrapper);
- if (varargsRes instanceof MemberAndArguments) {
- return (MemberAndArguments) varargsRes;
- }
- } else {
- varargsRes = null;
- }
-
- _ErrorDescriptionBuilder edb = new _ErrorDescriptionBuilder(
- toCompositeErrorMessage(
- (EmptyMemberAndArguments) fixArgsRes,
- (EmptyMemberAndArguments) varargsRes,
- tmArgs),
- "\nThe matching overload was searched among these members:\n",
- memberListToString());
- addMarkupBITipAfterNoNoMarchIfApplicable(edb, tmArgs);
- throw new _TemplateModelException(edb);
- }
-
- private Object[] toCompositeErrorMessage(
- final EmptyMemberAndArguments fixArgsEmptyRes, final EmptyMemberAndArguments varargsEmptyRes,
- List tmArgs) {
- final Object[] argsErrorMsg;
- if (varargsEmptyRes != null) {
- if (fixArgsEmptyRes == null || fixArgsEmptyRes.isNumberOfArgumentsWrong()) {
- argsErrorMsg = toErrorMessage(varargsEmptyRes, tmArgs);
- } else {
- argsErrorMsg = new Object[] {
- "When trying to call the non-varargs overloads:\n",
- toErrorMessage(fixArgsEmptyRes, tmArgs),
- "\nWhen trying to call the varargs overloads:\n",
- toErrorMessage(varargsEmptyRes, null)
- };
- }
- } else {
- argsErrorMsg = toErrorMessage(fixArgsEmptyRes, tmArgs);
- }
- return argsErrorMsg;
- }
-
- private Object[] toErrorMessage(EmptyMemberAndArguments res, List/*<TemplateModel>*/ tmArgs) {
- final Object[] unwrappedArgs = res.getUnwrappedArguments();
- return new Object[] {
- res.getErrorDescription(),
- tmArgs != null
- ? new Object[] {
- "\nThe FTL type of the argument values were: ", getTMActualParameterTypes(tmArgs), "." }
- : "",
- unwrappedArgs != null
- ? new Object[] {
- "\nThe Java type of the argument values were: ",
- getUnwrappedActualParameterTypes(unwrappedArgs) + "." }
- : ""};
- }
-
- private _DelayedConversionToString memberListToString() {
- return new _DelayedConversionToString(null) {
-
- @Override
- protected String doConversion(Object obj) {
- final Iterator fixArgMethodsIter = fixArgMethods.getMemberDescriptors();
- final Iterator varargMethodsIter = varargMethods != null ? varargMethods.getMemberDescriptors() : null;
-
- boolean hasMethods = fixArgMethodsIter.hasNext() || (varargMethodsIter != null && varargMethodsIter.hasNext());
- if (hasMethods) {
- StringBuilder sb = new StringBuilder();
- HashSet fixArgMethods = new HashSet();
- while (fixArgMethodsIter.hasNext()) {
- if (sb.length() != 0) sb.append(",\n");
- sb.append(" ");
- CallableMemberDescriptor callableMemberDesc = (CallableMemberDescriptor) fixArgMethodsIter.next();
- fixArgMethods.add(callableMemberDesc);
- sb.append(callableMemberDesc.getDeclaration());
- }
- if (varargMethodsIter != null) {
- while (varargMethodsIter.hasNext()) {
- CallableMemberDescriptor callableMemberDesc = (CallableMemberDescriptor) varargMethodsIter.next();
- if (!fixArgMethods.contains(callableMemberDesc)) {
- if (sb.length() != 0) sb.append(",\n");
- sb.append(" ");
- sb.append(callableMemberDesc.getDeclaration());
- }
- }
- }
- return sb.toString();
- } else {
- return "No members";
- }
- }
-
- };
- }
-
- /**
- * Adds tip to the error message if converting a {@link TemplateMarkupOutputModel} argument to {@link String} might
- * allows finding a matching overload.
- */
- private void addMarkupBITipAfterNoNoMarchIfApplicable(_ErrorDescriptionBuilder edb,
- List tmArgs) {
- for (int argIdx = 0; argIdx < tmArgs.size(); argIdx++) {
- Object tmArg = tmArgs.get(argIdx);
- if (tmArg instanceof TemplateMarkupOutputModel) {
- for (Iterator membDescs = fixArgMethods.getMemberDescriptors(); membDescs.hasNext();) {
- CallableMemberDescriptor membDesc = (CallableMemberDescriptor) membDescs.next();
- Class[] paramTypes = membDesc.getParamTypes();
-
- Class paramType = null;
- if (membDesc.isVarargs() && argIdx >= paramTypes.length - 1) {
- paramType = paramTypes[paramTypes.length - 1];
- if (paramType.isArray()) {
- paramType = paramType.getComponentType();
- }
- }
- if (paramType == null && argIdx < paramTypes.length) {
- paramType = paramTypes[argIdx];
- }
- if (paramType != null) {
- if (paramType.isAssignableFrom(String.class) && !paramType.isAssignableFrom(tmArg.getClass())) {
- edb.tip(SimpleMethodModel.MARKUP_OUTPUT_TO_STRING_TIP);
- return;
- }
- }
- }
- }
- }
- }
-
- private _DelayedConversionToString getTMActualParameterTypes(List arguments) {
- final String[] argumentTypeDescs = new String[arguments.size()];
- for (int i = 0; i < arguments.size(); i++) {
- argumentTypeDescs[i] = FTLUtil.getTypeDescription((TemplateModel) arguments.get(i));
- }
-
- return new DelayedCallSignatureToString(argumentTypeDescs) {
-
- @Override
- String argumentToString(Object argType) {
- return (String) argType;
- }
-
- };
- }
-
- private Object getUnwrappedActualParameterTypes(Object[] unwrappedArgs) {
- final Class[] argumentTypes = new Class[unwrappedArgs.length];
- for (int i = 0; i < unwrappedArgs.length; i++) {
- Object unwrappedArg = unwrappedArgs[i];
- argumentTypes[i] = unwrappedArg != null ? unwrappedArg.getClass() : null;
- }
-
- return new DelayedCallSignatureToString(argumentTypes) {
-
- @Override
- String argumentToString(Object argType) {
- return argType != null
- ? _ClassUtil.getShortClassName((Class) argType)
- : _ClassUtil.getShortClassNameOfObject(null);
- }
-
- };
- }
-
- private abstract class DelayedCallSignatureToString extends _DelayedConversionToString {
-
- public DelayedCallSignatureToString(Object[] argTypeArray) {
- super(argTypeArray);
- }
-
- @Override
- protected String doConversion(Object obj) {
- Object[] argTypes = (Object[]) obj;
-
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < argTypes.length; i++) {
- if (i != 0) sb.append(", ");
- sb.append(argumentToString(argTypes[i]));
- }
-
- return sb.toString();
- }
-
- abstract String argumentToString(Object argType);
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/051a0822/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsModel.java b/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsModel.java
deleted file mode 100644
index 9951c3e..0000000
--- a/src/main/java/org/apache/freemarker/core/model/impl/beans/OverloadedMethodsModel.java
+++ /dev/null
@@ -1,83 +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.util.Collections;
-import java.util.List;
-
-import org.apache.freemarker.core.model.TemplateMethodModelEx;
-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.impl.SimpleNumber;
-
-/**
- * Wraps a set of same-name overloaded methods behind {@link org.apache.freemarker.core.model.TemplateMethodModel} interface,
- * like if it was a single method, chooses among them behind the scenes on call-time based on the argument values.
- */
-public class OverloadedMethodsModel
-implements
- TemplateMethodModelEx,
- TemplateSequenceModel {
- private final Object object;
- private final OverloadedMethods overloadedMethods;
- private final BeansWrapper wrapper;
-
- OverloadedMethodsModel(Object object, OverloadedMethods overloadedMethods, BeansWrapper wrapper) {
- this.object = object;
- this.overloadedMethods = overloadedMethods;
- this.wrapper = wrapper;
- }
-
- /**
- * Invokes the method, passing it the arguments from the list. The actual
- * method to call from several overloaded methods will be chosen based
- * on the classes of the arguments.
- * @throws TemplateModelException if the method cannot be chosen
- * unambiguously.
- */
- @Override
- public Object exec(List arguments)
- throws TemplateModelException {
- MemberAndArguments maa = overloadedMethods.getMemberAndArguments(arguments, wrapper);
- try {
- return maa.invokeMethod(wrapper, object);
- } catch (Exception e) {
- if (e instanceof TemplateModelException) throw (TemplateModelException) e;
-
- throw _MethodUtil.newInvocationTemplateModelException(
- object,
- maa.getCallableMemberDescriptor(),
- e);
- }
- }
-
- @Override
- public TemplateModel get(int index) throws TemplateModelException {
- return (TemplateModel) exec(Collections.singletonList(
- new SimpleNumber(Integer.valueOf(index))));
- }
-
- @Override
- public int size() throws TemplateModelException {
- throw new TemplateModelException("?size is unsupported for " + getClass().getName());
- }
-}