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)); + } + +}
