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/03/12 14:42:30 UTC

incubator-freemarker git commit: DefaultObjectWrapper, only with its incompatible_improvements set to 2.3.26, wraps java.util.Enumeration-s into freemarker.template.DefaultEnumerationAdapter (a new class) instead of into freemarker.ext.beans.Enumeration

Repository: incubator-freemarker
Updated Branches:
  refs/heads/2.3-gae 593d35d32 -> 544e9105c


DefaultObjectWrapper, only with its incompatible_improvements set to 2.3.26, wraps java.util.Enumeration-s into  freemarker.template.DefaultEnumerationAdapter (a new class) instead of into freemarker.ext.beans.EnumerationModel (as far as useAdaptersForContainers is true, which is the default). This adapter is cleaner than EnumerationModel as it only implements the minimally required FTL type, which avoids some ambiguous situations. (Note that Java API methods aren't exposed anymore as subvariables; if you really need them, you can use ?api).


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/544e9105
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/544e9105
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/544e9105

Branch: refs/heads/2.3-gae
Commit: 544e9105c54c0afbdcea6d2eac00cf19aa0f4daa
Parents: 593d35d
Author: ddekany <dd...@apache.org>
Authored: Sun Mar 12 15:42:24 2017 +0100
Committer: ddekany <dd...@apache.org>
Committed: Sun Mar 12 15:42:24 2017 +0100

----------------------------------------------------------------------
 .../template/DefaultEnumerationAdapter.java     | 96 ++++++++++++++++++++
 .../template/DefaultObjectWrapper.java          | 16 ++++
 src/manual/en_US/book.xml                       | 22 ++++-
 .../template/DefaultObjectWrapperTest.java      | 40 ++++++++
 4 files changed, 172 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/544e9105/src/main/java/freemarker/template/DefaultEnumerationAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/DefaultEnumerationAdapter.java b/src/main/java/freemarker/template/DefaultEnumerationAdapter.java
new file mode 100644
index 0000000..570e0e8
--- /dev/null
+++ b/src/main/java/freemarker/template/DefaultEnumerationAdapter.java
@@ -0,0 +1,96 @@
+package freemarker.template;
+
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import freemarker.ext.util.WrapperTemplateModel;
+
+/**
+ * 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.
+ * 
+ * @since 2.3.26
+ */
+@SuppressWarnings("serial")
+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 enumeration
+     *            The enumeration to adapt; can't be {@code null}.
+     */
+    public static DefaultEnumerationAdapter adapt(Enumeration<?> enumeration, ObjectWrapper wrapper) {
+        return new DefaultEnumerationAdapter(enumeration, 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/544e9105/src/main/java/freemarker/template/DefaultObjectWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/DefaultObjectWrapper.java b/src/main/java/freemarker/template/DefaultObjectWrapper.java
index 1398be4..4c5a39d 100644
--- a/src/main/java/freemarker/template/DefaultObjectWrapper.java
+++ b/src/main/java/freemarker/template/DefaultObjectWrapper.java
@@ -22,6 +22,7 @@ package freemarker.template;
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -31,6 +32,7 @@ import org.w3c.dom.Node;
 
 import freemarker.ext.beans.BeansWrapper;
 import freemarker.ext.beans.BeansWrapperConfiguration;
+import freemarker.ext.beans.EnumerationModel;
 import freemarker.ext.dom.NodeModel;
 import freemarker.log.Logger;
 
@@ -68,6 +70,7 @@ public class DefaultObjectWrapper extends freemarker.ext.beans.BeansWrapper {
     private boolean useAdaptersForContainers;
     private boolean forceLegacyNonListCollections;
     private boolean iterableSupport;
+    private final boolean useAdapterForEnumerations;
     
     /**
      * Creates a new instance with the incompatible-improvements-version specified in
@@ -97,6 +100,13 @@ public class DefaultObjectWrapper extends freemarker.ext.beans.BeansWrapper {
      *                  won't cause the a later iteration (or further emptiness check) to fail anymore. Earlier, in
      *                  certain situations, the second operation has failed saying that the iterator "can be listed only
      *                  once".  
+     *              <li>2.3.26 (or higher): {@link Enumeration}-s are wrapped into {@link DefaultEnumerationAdapter}
+     *                  instead of into {@link EnumerationModel} (as far as
+     *                  {@link #setUseAdaptersForContainers(boolean) useAdaptersForContainers} is {@code true}, which is
+     *                  the default). This adapter is cleaner than {@link EnumerationModel} as it only implements the
+     *                  minimally required FTL type, which avoids some ambiguous situations. (Note that Java API methods
+     *                  aren't exposed anymore as subvariables; if you really need them, you can use {@code ?api}). 
+     *                  </li>
      *            </ul>
      * 
      * @since 2.3.21
@@ -117,6 +127,8 @@ public class DefaultObjectWrapper extends freemarker.ext.beans.BeansWrapper {
                 ? (DefaultObjectWrapperConfiguration) bwCfg
                 : new DefaultObjectWrapperConfiguration(bwCfg.getIncompatibleImprovements()) { }; 
         useAdaptersForContainers = dowDowCfg.getUseAdaptersForContainers();
+        useAdapterForEnumerations = useAdaptersForContainers
+                && getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_26;
         forceLegacyNonListCollections = dowDowCfg.getForceLegacyNonListCollections();
         iterableSupport = dowDowCfg.getIterableSupport();
         finalizeConstruction(writeProtected);
@@ -226,9 +238,13 @@ public class DefaultObjectWrapper extends freemarker.ext.beans.BeansWrapper {
                     ? (TemplateModel) DefaultIteratorAdapter.adapt((Iterator<?>) obj, this)
                     : (TemplateModel) new SimpleCollection((Iterator<?>) obj, this);
         }
+        if (useAdapterForEnumerations && obj instanceof Enumeration) {
+            return DefaultEnumerationAdapter.adapt((Enumeration<?>) obj, this);
+        }        
         if (iterableSupport && obj instanceof Iterable) {
             return DefaultIterableAdapter.adapt((Iterable<?>) obj, this);
         }
+        
         return handleUnknownType(obj);
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/544e9105/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 3e0814f..beee581 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -7,9 +7,9 @@
   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
@@ -26887,6 +26887,24 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
             </listitem>
 
             <listitem>
+              <para><literal>DefaultObjectWrapper</literal>, only with its
+              <literal>incompatible_improvements</literal> set to 2.3.26
+              (<link linkend="topic.defaultObjectWrapperIcI">see how
+              here...</link>), wraps
+              <literal>java.util.Enumeration</literal>-s into
+              <literal>freemarker.template.DefaultEnumerationAdapter</literal>
+              (a new class) instead of into
+              <literal>freemarker.ext.beans.EnumerationModel</literal> (as far
+              as <literal>useAdaptersForContainers</literal> is
+              <literal>true</literal>, which is the default). This adapter is
+              cleaner than <literal>EnumerationModel</literal> as it only
+              implements the minimally required FTL type, which avoids some
+              ambiguous situations. (Note that Java API methods aren't exposed
+              anymore as subvariables; if you really need them, you can use
+              <literal>?api</literal>).</para>
+            </listitem>
+
+            <listitem>
               <para>Better error messages when someone tries to get an invalid
               <literal>@@<replaceable>...</replaceable></literal> subvariable
               of an XML DOM node. (Now it's not issued by the XPath

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/544e9105/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/template/DefaultObjectWrapperTest.java b/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
index 5d3ad2b..4be352a 100644
--- a/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
+++ b/src/test/java/freemarker/template/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;
@@ -53,6 +54,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 import freemarker.ext.beans.BeansWrapper;
+import freemarker.ext.beans.EnumerationModel;
 import freemarker.ext.beans.HashAdapter;
 import freemarker.ext.util.WrapperTemplateModel;
 
@@ -943,6 +945,44 @@ public class DefaultObjectWrapperTest {
             assertTemplateOutput(OW22IS, iterable, listingFTL, "a, b, c");
         }
     }
+
+    @Test
+    public void testNoEnumerationAdapter() throws TemplateModelException {
+         DefaultObjectWrapper ow = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_25).build();
+         Vector<String> vector = new Vector<String>();
+         vector.add("a");
+         vector.add("b");
+         
+         TemplateModel wrappedEnumeration = ow.wrap(vector.elements());
+         assertThat(wrappedEnumeration, instanceOf(EnumerationModel.class));
+         EnumerationModel enumModel = (EnumerationModel) wrappedEnumeration;
+         assertNotNull(enumModel.get("nextElement"));
+    }
+    
+    @Test
+    public void testEnumerationAdapter() throws TemplateModelException {
+         DefaultObjectWrapper ow = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_26).build();
+         Vector<String> vector = new Vector<String>();
+         vector.add("a");
+         vector.add("b");
+         
+         TemplateModel wrappedEnumeration = ow.wrap(vector.elements());
+         assertThat(wrappedEnumeration, instanceOf(DefaultEnumerationAdapter.class));
+         DefaultEnumerationAdapter enumAdapter = (DefaultEnumerationAdapter) wrappedEnumeration;
+         TemplateModelIterator iterator = enumAdapter.iterator();
+         assertTrue(iterator.hasNext());
+         assertEquals("a", ((TemplateScalarModel) iterator.next()).getAsString());
+         assertTrue(iterator.hasNext());
+         assertEquals("b", ((TemplateScalarModel) iterator.next()).getAsString());
+         assertFalse(iterator.hasNext());
+         
+         iterator = enumAdapter.iterator();
+         try {
+             iterator.hasNext();
+         } catch (TemplateException e) {
+             assertThat(e.getMessage(), containsStringIgnoringCase("only once"));
+         }
+    }
     
     @Test
     public void assertCanWrapDOM() throws SAXException, IOException, ParserConfigurationException,