From 58d30bb508650e6f596cc1df5bae1586aa41b3a1 Mon Sep 17 00:00:00 2001
From: buczekp <buczekp>
Date: Tue, 16 Nov 2010 09:27:26 +0000
Subject: [PATCH] [LMS-1767] quick fix of multiple escaping of related objects
 (e.g. properties with value being a Vocabulary Term or XML)

SVN: 18730
---
 .../utilities/ReflectingStringEscaper.java    | 102 ++++++++++++++++--
 1 file changed, 94 insertions(+), 8 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 74df4c927ff..cf1384d4cbc 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaper.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/ReflectingStringEscaper.java
@@ -21,6 +21,10 @@ import java.util.Collections;
 import java.util.HashSet;
 
 import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
 
 /**
  * Performs HTML escaping the string fields of an object. If desired, users can restrict the fields
@@ -31,6 +35,8 @@ import org.apache.commons.lang.StringEscapeUtils;
  */
 public class ReflectingStringEscaper
 {
+    private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
+            ReflectingStringEscaper.class);
 
     /**
      * Escape all the string fields on the bean.
@@ -47,9 +53,17 @@ public class ReflectingStringEscaper
      */
     public static <T> T escapeDeep(T bean)
     {
-        ReflectingStringEscaperUnrestricted<T> escaper =
-                new ReflectingStringEscaperUnrestricted<T>(true, bean);
-        return escaper.escape();
+        long time = System.currentTimeMillis();
+        try
+        {
+            ReflectingStringEscaperUnrestricted<T> escaper =
+                    new ReflectingStringEscaperUnrestricted<T>(true, bean);
+            return escaper.escape();
+        } finally
+        {
+            operationLog.info((System.currentTimeMillis() - time) + "ms for escaping "
+                    + (bean == null ? "" : bean.getClass()));
+        }
     }
 
     /**
@@ -84,10 +98,6 @@ class ReflectingStringEscaperImpl<T>
 
     protected final T bean;
 
-    /**
-     *
-     *
-     */
     protected ReflectingStringEscaperImpl(boolean isDeep, T bean)
     {
         this.isDeep = isDeep;
@@ -128,7 +138,9 @@ class ReflectingStringEscaperRestricted<T> extends ReflectingStringEscaperImpl<T
                 return null;
             }
             if (escapedProperties.contains(fieldOrNull.getName()))
+            {
                 return StringEscapeUtils.escapeHtml(value);
+            }
             return null;
         }
     }
@@ -154,11 +166,77 @@ class ReflectingStringEscaperRestricted<T> extends ReflectingStringEscaperImpl<T
 class ReflectingStringEscaperUnrestricted<T> extends ReflectingStringEscaperImpl<T>
 {
 
+    private static class Pair
+    {
+        Object first;
+
+        Object second;
+
+        public Pair(Object first, Object second)
+        {
+            super();
+            this.first = first;
+            this.second = second;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((first == null) ? 0 : first.hashCode());
+            result = prime * result + ((second == null) ? 0 : second.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            if (this == obj)
+            {
+                return true;
+            }
+            if (obj == null)
+            {
+                return false;
+            }
+            if (!(obj instanceof Pair))
+            {
+                return false;
+            }
+            Pair other = (Pair) obj;
+            if (first == null)
+            {
+                if (other.first != null)
+                {
+                    return false;
+                }
+            } else if (!first.equals(other.first))
+            {
+                return false;
+            }
+            if (second == null)
+            {
+                if (other.second != null)
+                {
+                    return false;
+                }
+            } else if (!second.equals(other.second))
+            {
+                return false;
+            }
+            return true;
+        }
+
+    }
+
     private static class Visitor implements ReflectionStringTraverser.ReflectionFieldVisitor
     {
 
         private final HashSet<String> unescapedProperties;
 
+        private final HashSet<Pair> seenObjectFields = new HashSet<Pair>();
+
         private Visitor()
         {
             unescapedProperties = new HashSet<String>();
@@ -168,8 +246,17 @@ class ReflectingStringEscaperUnrestricted<T> extends ReflectingStringEscaperImpl
 
         public String tryVisit(String value, Object object, Field fieldOrNull)
         {
+            Pair pair = new Pair(object, fieldOrNull);
+            if (seenObjectFields.contains(pair))
+            {
+                return null;
+            } else
+            {
+                seenObjectFields.add(pair);
+            }
             if (null == fieldOrNull)
             {
+                // TODO 2010-11-16, Piotr Buczek: how is this possible?
                 return StringEscapeUtils.escapeHtml(value);
             }
 
@@ -180,7 +267,6 @@ class ReflectingStringEscaperUnrestricted<T> extends ReflectingStringEscaperImpl
             }
 
             return StringEscapeUtils.escapeHtml(value);
-
         }
     }
 
-- 
GitLab