From 94e0a434e9906732f088ac6fdaa8b733359be16a Mon Sep 17 00:00:00 2001
From: cramakri <cramakri>
Date: Tue, 9 Nov 2010 09:17:54 +0000
Subject: [PATCH] LMS-1767 Added control of whether escaping is shallow or
 deep.

SVN: 18592
---
 .../utilities/ReflectingStringEscaper.java    | 29 ++++++++++-----
 .../utilities/ReflectionStringTraverser.java  | 31 +++++++++++++---
 .../ReflectingStringEscaperTest.java          | 37 ++++++++++++++++++-
 ...ReflectionPrimitiveFieldTraverserTest.java |  2 +-
 4 files changed, 81 insertions(+), 18 deletions(-)

diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaper.java b/common/source/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaper.java
index d25dc58c2d5..30c2635cf53 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaper.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaper.java
@@ -30,13 +30,22 @@ import org.apache.commons.lang.StringEscapeUtils;
  */
 public class ReflectingStringEscaper<T>
 {
-    public static <T> T escape(T bean, String... escapedProperties)
+    public static <T> T escapeShallow(T bean, String... escapedProperties)
     {
         ReflectingStringEscaper<T> escaper =
-                new ReflectingStringEscaper<T>(bean, escapedProperties);
+                new ReflectingStringEscaper<T>(false, bean, escapedProperties);
         return escaper.escape();
     }
 
+    public static <T> T escapeDeep(T bean, String... escapedProperties)
+    {
+        ReflectingStringEscaper<T> escaper =
+                new ReflectingStringEscaper<T>(true, bean, escapedProperties);
+        return escaper.escape();
+    }
+
+    private final boolean isDeep;
+
     private final T bean;
 
     private final HashSet<String> escapedProperties;
@@ -46,11 +55,6 @@ public class ReflectingStringEscaper<T>
 
         public String tryVisit(String value, Object object, Field fieldOrNull)
         {
-            // Only change the value on the top-level object
-            if (object != bean)
-            {
-                return null;
-            }
             // Only change the value if the name of the field is in the list provided
             if (null == fieldOrNull)
             {
@@ -62,8 +66,9 @@ public class ReflectingStringEscaper<T>
         }
     }
 
-    private ReflectingStringEscaper(T bean, String... escapedProperties)
+    private ReflectingStringEscaper(boolean isDeep, T bean, String... escapedProperties)
     {
+        this.isDeep = isDeep;
         this.bean = bean;
         this.escapedProperties = new HashSet<String>();
         Collections.addAll(this.escapedProperties, escapedProperties);
@@ -71,7 +76,13 @@ public class ReflectingStringEscaper<T>
 
     private T escape()
     {
-        ReflectionStringTraverser.traverse(bean, new Visitor());
+        if (isDeep)
+        {
+            ReflectionStringTraverser.traverseDeep(bean, new Visitor());
+        } else
+        {
+            ReflectionStringTraverser.traverseShallow(bean, new Visitor());
+        }
         return bean;
     }
 }
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/ReflectionStringTraverser.java b/common/source/java/ch/systemsx/cisd/common/utilities/ReflectionStringTraverser.java
index 2ee03464e4b..5586e624645 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/ReflectionStringTraverser.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/ReflectionStringTraverser.java
@@ -26,8 +26,9 @@ import java.util.List;
 import java.util.Set;
 
 /**
- * Allows to change all non-final and non-static strings referenced within the specified object.
- * Traverses the object recursively. Handles lists and sets of strings.
+ * Allows to change all non-final and non-static strings referenced within the specified object. If
+ * isDeep is true, traverses the object recursively; otherwise, performs a shallow traversal.
+ * Handles lists and sets of strings.
  * 
  * @author Tomasz Pylak
  */
@@ -45,12 +46,19 @@ public class ReflectionStringTraverser
     }
 
     /** cannot be called for primitive types or collections */
-    public static void traverse(Object object, ReflectionFieldVisitor fieldVisitor)
+    public static void traverseDeep(Object object, ReflectionFieldVisitor fieldVisitor)
     {
         Class<?> clazz = object.getClass();
         new ReflectionStringTraverser(fieldVisitor).traverseMutable(object, clazz);
     }
 
+    /** cannot be called for primitive types or collections */
+    public static void traverseShallow(Object object, ReflectionFieldVisitor fieldVisitor)
+    {
+        Class<?> clazz = object.getClass();
+        new ReflectionStringTraverser(fieldVisitor, false).traverseMutable(object, clazz);
+    }
+
     // mutable classes are arrays and classes which are not primitives or collections of primitive
     // types
     private void traverseMutable(Object object, Class<?> clazz)
@@ -71,9 +79,17 @@ public class ReflectionStringTraverser
 
     private final ReflectionFieldVisitor visitor;
 
+    private final boolean isDeep;
+
     private ReflectionStringTraverser(ReflectionFieldVisitor fieldVisitor)
+    {
+        this(fieldVisitor, true);
+    }
+
+    private ReflectionStringTraverser(ReflectionFieldVisitor fieldVisitor, boolean isDeep)
     {
         this.visitor = fieldVisitor;
+        this.isDeep = isDeep;
     }
 
     /**
@@ -118,10 +134,10 @@ public class ReflectionStringTraverser
         }
         Class<?> clazz = fieldValue.getClass();
 
-        if (clazz.isArray())
+        if (clazz.isArray() && isDeep)
         {
             traverseArray(fieldValue);
-        } else if (isCollection(fieldValue))
+        } else if (isCollection(fieldValue) && isDeep)
         {
             traverseCollectionField(object, field, (Collection<?>) fieldValue);
         } else if (clazz.isPrimitive())
@@ -136,7 +152,10 @@ public class ReflectionStringTraverser
             }
         } else
         {
-            traverseFields(fieldValue, clazz);
+            if (isDeep)
+            {
+                traverseFields(fieldValue, clazz);
+            }
         }
     }
 
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaperTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaperTest.java
index 42ff65c08c5..03a6b0c4273 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaperTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaperTest.java
@@ -31,20 +31,53 @@ public class ReflectingStringEscaperTest extends AssertJUnit
         private String bar;
 
         private String baz;
+
+        private TestBean wrappedBean;
+    }
+
+    @Test
+    public void testShallowEscaper()
+    {
+        TestBean bean = new TestBean();
+        bean.foo = "<a>foo</a>";
+        bean.bar = "<b>bar</b>";
+        bean.baz = "<i>baz</i>";
+        bean.wrappedBean = new TestBean();
+        bean.wrappedBean.foo = "<a>foo</a>";
+        bean.wrappedBean.bar = "<b>bar</b>";
+        bean.wrappedBean.baz = "<i>baz</i>";
+
+        TestBean escaped = ReflectingStringEscaper.escapeShallow(bean, "foo", "baz");
+        assertEquals(bean, escaped);
+        assertEquals("&lt;a&gt;foo&lt;/a&gt;", bean.foo);
+        assertEquals("<b>bar</b>", bean.bar);
+        assertEquals("&lt;i&gt;baz&lt;/i&gt;", bean.baz);
+
+        assertEquals("<a>foo</a>", bean.wrappedBean.foo);
+        assertEquals("<b>bar</b>", bean.wrappedBean.bar);
+        assertEquals("<i>baz</i>", bean.wrappedBean.baz);
     }
 
     @Test
-    public void testEscaper()
+    public void testDeepEscaper()
     {
         TestBean bean = new TestBean();
         bean.foo = "<a>foo</a>";
         bean.bar = "<b>bar</b>";
         bean.baz = "<i>baz</i>";
+        bean.wrappedBean = new TestBean();
+        bean.wrappedBean.foo = "<a>foo</a>";
+        bean.wrappedBean.bar = "<b>bar</b>";
+        bean.wrappedBean.baz = "<i>baz</i>";
 
-        TestBean escaped = ReflectingStringEscaper.escape(bean, "foo", "baz");
+        TestBean escaped = ReflectingStringEscaper.escapeDeep(bean, "foo", "baz");
         assertEquals(bean, escaped);
         assertEquals("&lt;a&gt;foo&lt;/a&gt;", bean.foo);
         assertEquals("<b>bar</b>", bean.bar);
         assertEquals("&lt;i&gt;baz&lt;/i&gt;", bean.baz);
+
+        assertEquals("&lt;a&gt;foo&lt;/a&gt;", bean.wrappedBean.foo);
+        assertEquals("<b>bar</b>", bean.wrappedBean.bar);
+        assertEquals("&lt;i&gt;baz&lt;/i&gt;", bean.wrappedBean.baz);
     }
 }
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ReflectionPrimitiveFieldTraverserTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ReflectionPrimitiveFieldTraverserTest.java
index e771a09ee2c..1d87d23c6e6 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ReflectionPrimitiveFieldTraverserTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ReflectionPrimitiveFieldTraverserTest.java
@@ -82,7 +82,7 @@ public class ReflectionPrimitiveFieldTraverserTest extends AssertJUnit
         object.stringArray = new String[]
             { "stringArray1" };
 
-        ReflectionStringTraverser.traverse(object, new ReflectionStringCapitalizerVisitor());
+        ReflectionStringTraverser.traverseDeep(object, new ReflectionStringCapitalizerVisitor());
 
         assertEquals("OBJECT", object.text);
 
-- 
GitLab