Author: nbelaevski
Date: 2010-04-29 13:16:49 -0400 (Thu, 29 Apr 2010)
New Revision: 16847
Modified:
branches/enterprise/3.3.X/framework/api/src/main/java/org/ajax4jsf/Messages.java
branches/enterprise/3.3.X/framework/api/src/main/java/org/ajax4jsf/javascript/ScriptUtils.java
branches/enterprise/3.3.X/framework/api/src/test/java/org/ajax4jsf/framework/util/javascript/ScriptUtilsTest.java
branches/enterprise/3.3.X/framework/impl/src/main/resources/org/ajax4jsf/messages.properties
Log:
https://jira.jboss.org/jira/browse/RFPL-540
Modified:
branches/enterprise/3.3.X/framework/api/src/main/java/org/ajax4jsf/Messages.java
===================================================================
---
branches/enterprise/3.3.X/framework/api/src/main/java/org/ajax4jsf/Messages.java 2010-04-29
15:54:40 UTC (rev 16846)
+++
branches/enterprise/3.3.X/framework/api/src/main/java/org/ajax4jsf/Messages.java 2010-04-29
17:16:49 UTC (rev 16847)
@@ -365,7 +365,8 @@
public static final String DATASCROLLER_PAGE_MISSING =
"DATASCROLLER_PAGE_MISSING";
public static final String DATASCROLLER_PAGES_DIFFERENT =
"DATASCROLLER_PAGES_DIFFERENT";
public static final String COMPONENT_CONVERSION_ERROR =
"COMPONENT_CONVERSION_ERROR";
-
+ public static final String JAVASCRIPT_CIRCULAR_REFERENCE =
"JAVASCRIPT_CIRCULAR_REFERENCE";
+
public static void main(String[] args) {
String m = getMessage(INVALID_ATTRIBUTE_VALUE, "A", "B");
System.out.println(m);
Modified:
branches/enterprise/3.3.X/framework/api/src/main/java/org/ajax4jsf/javascript/ScriptUtils.java
===================================================================
---
branches/enterprise/3.3.X/framework/api/src/main/java/org/ajax4jsf/javascript/ScriptUtils.java 2010-04-29
15:54:40 UTC (rev 16846)
+++
branches/enterprise/3.3.X/framework/api/src/main/java/org/ajax4jsf/javascript/ScriptUtils.java 2010-04-29
17:16:49 UTC (rev 16847)
@@ -24,13 +24,19 @@
import java.beans.PropertyDescriptor;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
+import java.text.MessageFormat;
import java.util.Collection;
+import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.MissingResourceException;
import javax.faces.FacesException;
+import org.ajax4jsf.Messages;
import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
/**
* @author shura (latest modification by $Author: alexsmirnov $)
@@ -39,6 +45,8 @@
*/
public class ScriptUtils {
+ private static final Log LOG = LogFactory.getLog(ScriptUtils.class);
+
/**
* This is utility class, don't instantiate.
*/
@@ -52,10 +60,31 @@
* @return
*/
public static String toScript(Object obj) {
- if (null == obj) {
- return "null";
+ Map<Object, Boolean> cycleBusterMap = new IdentityHashMap<Object,
Boolean>();
+ return toScript(obj, cycleBusterMap);
+ }
+
+ private static String toScript(Object obj, Map<Object, Boolean> cycleBusterMap) {
+ String result;
+ Boolean cycleBusterValue = cycleBusterMap.put(obj, Boolean.TRUE);
+
+ if (cycleBusterValue != null) {
+ if (LOG.isDebugEnabled()) {
+ String formattedMessage;
+ try {
+ formattedMessage =
Messages.getMessage(Messages.JAVASCRIPT_CIRCULAR_REFERENCE, obj);
+ } catch (MissingResourceException e) {
+ //ignore exception: workaround for unit tests
+ formattedMessage = MessageFormat.format("Circular reference
serializing object to JS: {0}", obj);
+ }
+
+ LOG.debug(formattedMessage);
+ }
+ result = "null";
+ } else if (null == obj) {
+ result = "null";
} else if (obj instanceof ScriptString) {
- return ((ScriptString) obj).toScript();
+ result = ((ScriptString) obj).toScript();
} else if (obj.getClass().isArray()) {
StringBuilder ret = new StringBuilder("[");
boolean first = true;
@@ -64,11 +93,11 @@
if (!first) {
ret.append(',');
}
- ret.append(toScript(element));
+ ret.append(toScript(element, cycleBusterMap));
first = false;
}
- return ret.append("] ").toString();
- } else if (obj instanceof Collection) {
+ result = ret.append("] ").toString();
+ } else if (obj instanceof Collection<?>) {
// Collections put as JavaScript array.
@SuppressWarnings("unchecked")
@@ -81,11 +110,11 @@
if (!first) {
ret.append(',');
}
- ret.append(toScript(element));
+ ret.append(toScript(element, cycleBusterMap));
first = false;
}
- return ret.append("] ").toString();
- } else if (obj instanceof Map) {
+ result = ret.append("] ").toString();
+ } else if (obj instanceof Map<?, ?>) {
// Maps put as JavaScript hash.
@SuppressWarnings("unchecked")
@@ -100,23 +129,23 @@
addEncodedString(ret, entry.getKey());
ret.append(":");
- ret.append(toScript(entry.getValue()));
+ ret.append(toScript(entry.getValue(), cycleBusterMap));
first = false;
}
- return ret.append("} ").toString();
+ result = ret.append("} ").toString();
} else if (obj instanceof Number || obj instanceof Boolean) {
// numbers and boolean put as-is, without conversion
- return obj.toString();
+ result = obj.toString();
} else if (obj instanceof String) {
// all other put as encoded strings.
StringBuilder ret = new StringBuilder();
addEncodedString(ret, obj);
- return ret.toString();
- } else if (obj instanceof Enum) {
+ result = ret.toString();
+ } else if (obj instanceof Enum<?>) {
// all other put as encoded strings.
StringBuilder ret = new StringBuilder();
addEncodedString(ret, obj);
- return ret.toString();
+ result = ret.toString();
} else if (obj.getClass().getName().startsWith("java.sql.")) {
StringBuilder ret = new StringBuilder("{");
boolean first = true;
@@ -139,37 +168,43 @@
addEncodedString(ret, key);
ret.append(":");
- ret.append(toScript(value));
+ ret.append(toScript(value, cycleBusterMap));
first = false;
}
- return ret.append("} ").toString();
- }
-
- // All other objects threaded as Java Beans.
- try {
- StringBuilder ret = new StringBuilder("{");
- PropertyDescriptor[] propertyDescriptors = PropertyUtils
- .getPropertyDescriptors(obj);
- boolean first = true;
- for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
- String key = propertyDescriptor.getName();
- if ("class".equals(key)) {
- continue;
+ result = ret.append("} ").toString();
+ } else {
+ // All other objects threaded as Java Beans.
+ try {
+ StringBuilder ret = new StringBuilder("{");
+ PropertyDescriptor[] propertyDescriptors = PropertyUtils
+ .getPropertyDescriptors(obj);
+ boolean first = true;
+ for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+ String key = propertyDescriptor.getName();
+ if ("class".equals(key)) {
+ continue;
+ }
+ if (!first) {
+ ret.append(',');
+ }
+ addEncodedString(ret, key);
+ ret.append(":");
+ ret.append(toScript(PropertyUtils.getProperty(obj, key),
cycleBusterMap));
+ first = false;
}
- if (!first) {
- ret.append(',');
- }
- addEncodedString(ret, key);
- ret.append(":");
- ret.append(toScript(PropertyUtils.getProperty(obj, key)));
- first = false;
+ result = ret.append("} ").toString();
+ } catch (Exception e) {
+ throw new FacesException(
+ "Error in conversion Java Object to JavaScript", e);
}
- return ret.append("} ").toString();
- } catch (Exception e) {
- throw new FacesException(
- "Error in conversion Java Object to JavaScript", e);
}
+
+ if (cycleBusterValue == null) {
+ cycleBusterMap.remove(obj);
+ }
+
+ return result;
}
public static void addEncodedString(StringBuilder buff, Object obj) {
Modified:
branches/enterprise/3.3.X/framework/api/src/test/java/org/ajax4jsf/framework/util/javascript/ScriptUtilsTest.java
===================================================================
---
branches/enterprise/3.3.X/framework/api/src/test/java/org/ajax4jsf/framework/util/javascript/ScriptUtilsTest.java 2010-04-29
15:54:40 UTC (rev 16846)
+++
branches/enterprise/3.3.X/framework/api/src/test/java/org/ajax4jsf/framework/util/javascript/ScriptUtilsTest.java 2010-04-29
17:16:49 UTC (rev 16847)
@@ -23,34 +23,37 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
+import junit.framework.TestCase;
+
import org.ajax4jsf.javascript.ScriptUtils;
-import junit.framework.TestCase;
-
/**
* @author shura
- *
+ *
*/
public class ScriptUtilsTest extends TestCase {
/**
* @author shura
- *
+ *
*/
public static class Bean {
-
+
int _integer;
boolean _bool;
Object _foo;
-
+
public Bean() {
}
+
/**
* @param ineger
* @param bool
@@ -61,44 +64,103 @@
this._bool = bool;
this._foo = foo;
}
+
/**
* @return the bool
*/
public boolean isBool() {
return this._bool;
}
+
/**
- * @param bool the bool to set
+ * @param bool
+ * the bool to set
*/
public void setBool(boolean bool) {
this._bool = bool;
}
+
/**
* @return the ineger
*/
public int getInteger() {
return this._integer;
}
+
/**
- * @param ineger the ineger to set
+ * @param ineger
+ * the ineger to set
*/
public void setInteger(int ineger) {
this._integer = ineger;
}
+
/**
* @return the foo
*/
public Object getFoo() {
return this._foo;
}
+
/**
- * @param foo the foo to set
+ * @param foo
+ * the foo to set
*/
public void setFoo(Object foo) {
this._foo = foo;
}
}
+ public static class ReferencedBean {
+
+ private String name;
+
+ private ReferenceHolderBean parent;
+
+ public ReferencedBean(String name, ReferenceHolderBean parent) {
+ super();
+ this.name = name;
+ this.parent = parent;
+ }
+
+ public ReferenceHolderBean getParent() {
+ return parent;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public static class ReferenceHolderBean {
+
+ private String name;
+
+ private Object reference;
+
+ public ReferenceHolderBean(String name) {
+ super();
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Object getReference() {
+ return reference;
+ }
+
+ public void setReference(Object reference) {
+ this.reference = reference;
+ }
+
+ }
+
+ private static String dehydrate(String s) {
+ return s != null ? s.replaceAll("\\s", "") : s;
+ }
+
/**
* @param name
*/
@@ -107,7 +169,8 @@
}
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
public void testStringToScript() {
Object obj = "foo";
@@ -115,42 +178,49 @@
}
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
public void testArrayToScript() {
- int[] obj = {1,2,3,4,5};
+ int[] obj = { 1, 2, 3, 4, 5 };
assertEquals("[1,2,3,4,5] ", ScriptUtils.toScript(obj));
}
public void testSqlDate() {
- java.sql.Time obj = new java.sql.Time(1);
- assertNotNull(ScriptUtils.toScript(obj));
+ java.sql.Time obj = new java.sql.Time(1);
+ assertNotNull(ScriptUtils.toScript(obj));
- java.sql.Date obj1 = new java.sql.Date(1);
- assertNotNull(ScriptUtils.toScript(obj1));
+ java.sql.Date obj1 = new java.sql.Date(1);
+ assertNotNull(ScriptUtils.toScript(obj1));
}
-
+
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
public void testTwoDimentionalArrayToScript() {
- int[][] obj = {{1,2},{3,4}};
+ int[][] obj = { { 1, 2 }, { 3, 4 } };
assertEquals("[[1,2] ,[3,4] ] ", ScriptUtils.toScript(obj));
}
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
public void testTwoDimentionalStringArrayToScript() {
- String[][] obj =
{{"one","two"},{"three","four"}};
- assertEquals("[['one','two'] ,['three','four'] ]
", ScriptUtils.toScript(obj));
+ String[][] obj = { { "one", "two" }, { "three",
"four" } };
+ assertEquals("[['one','two'] ,['three','four'] ]
", ScriptUtils
+ .toScript(obj));
Map<String, Object> map = new TreeMap<String, Object>();
map.put("a", obj);
map.put("b", "c");
- assertEquals("{'a':[['one','two']
,['three','four'] ] ,'b':'c'} ",
ScriptUtils.toScript(map));
+ assertEquals("{'a':[['one','two']
,['three','four'] ] ,'b':'c'} ",
+ ScriptUtils.toScript(map));
}
+
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
public void testListToScript() {
List<Integer> obj = new ArrayList<Integer>();
@@ -163,7 +233,8 @@
}
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
public void testSetToScript() {
Set<Integer> obj = new TreeSet<Integer>();
@@ -174,35 +245,47 @@
obj.add(new Integer(5));
assertEquals("[1,2,3,4,5] ", ScriptUtils.toScript(obj));
}
+
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
public void testObjectArrayToScript() {
- Bean[] obj = {new Bean(1,true,"foo"),new Bean(2,false,"bar")};
- assertEquals("[{'bool':true,'foo':'foo',\'integer\':1}
,{'bool':false,'foo':'bar','integer':2} ] ",
ScriptUtils.toScript(obj));
+ Bean[] obj = { new Bean(1, true, "foo"), new Bean(2, false, "bar")
};
+ assertEquals(
+ "[{'bool':true,'foo':'foo',\'integer\':1}
,{'bool':false,'foo':'bar','integer':2} ] ",
+ ScriptUtils.toScript(obj));
}
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
public void testObjectListToScript() {
- Bean[] array = {new Bean(1,true,"foo"),new Bean(2,false,"bar")};
+ Bean[] array = { new Bean(1, true, "foo"), new Bean(2, false,
"bar") };
List<Bean> obj = Arrays.asList(array);
- assertEquals("[{'bool':true,'foo':'foo',\'integer\':1}
,{'bool':false,'foo':'bar','integer':2} ] ",
ScriptUtils.toScript(obj));
+ assertEquals(
+ "[{'bool':true,'foo':'foo',\'integer\':1}
,{'bool':false,'foo':'bar','integer':2} ] ",
+ ScriptUtils.toScript(obj));
}
-
+
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#toScript(java.lang.Object)}.
*/
public void testMapToScript() {
TreeMap<String, String> obj = new TreeMap<String, String>();
obj.put("a", "foo");
obj.put("b", "bar");
obj.put("c", "baz");
- assertEquals("{'a':'foo','b':'bar','c':'baz'}
", ScriptUtils.toScript(obj));
+ assertEquals("{'a':'foo','b':'bar','c':'baz'}
", ScriptUtils
+ .toScript(obj));
}
+
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#addEncodedString(java.lang.StringBuffer,
java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#addEncodedString(java.lang.StringBuffer,
java.lang.Object)}
+ * .
*/
public void testAddEncodedString() {
StringBuilder buff = new StringBuilder();
@@ -211,7 +294,9 @@
}
/**
- * Test method for {@link
org.ajax4jsf.javascript.ScriptUtils#addEncoded(java.lang.StringBuffer,
java.lang.Object)}.
+ * Test method for
+ * {@link org.ajax4jsf.javascript.ScriptUtils#addEncoded(java.lang.StringBuffer,
java.lang.Object)}
+ * .
*/
public void testAddEncoded() {
StringBuilder buff = new StringBuilder();
@@ -219,5 +304,55 @@
assertEquals("foo\\\"\\\'", buff.toString());
}
+ public void testCircularReferenceBeans() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ assertEquals(dehydrate("{'name': 'child', 'parent':
{'name': 'parent', 'reference': null}}"),
+ dehydrate(ScriptUtils.toScript(child)));
+ }
+
+ public void testCircularReferenceViaProperty() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ parent.setReference(child);
+
+ assertEquals(dehydrate("{'name': 'parent', 'reference':
{'name': 'child', 'parent': null}}"),
+ dehydrate(ScriptUtils.toScript(parent)));
+ }
+ public void testCircularReferenceViaArray() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ parent.setReference(new Object[] {child});
+
+ assertEquals(dehydrate("{'name': 'parent', 'reference':
[{'name': 'child', 'parent': null}]}"),
+ dehydrate(ScriptUtils.toScript(parent)));
+ }
+
+ public void testCircularReferenceViaCollection() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ Collection<Object> set = new ArrayList<Object>();
+ set.add(child);
+ parent.setReference(set);
+
+ assertEquals(dehydrate("{'name': 'parent', 'reference':
[{'name': 'child', 'parent': null}]}"),
+ dehydrate(ScriptUtils.toScript(parent)));
+ }
+
+ public void testCircularReferenceViaMap() throws Exception {
+ ReferenceHolderBean parent = new ReferenceHolderBean("parent");
+ ReferencedBean child = new ReferencedBean("child", parent);
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put("key", child);
+ parent.setReference(map);
+
+ assertEquals(dehydrate("{'name': 'parent', 'reference':
{'key': {'name': 'child', 'parent': null}}}"),
+ dehydrate(ScriptUtils.toScript(parent)));
+ }
}
Modified:
branches/enterprise/3.3.X/framework/impl/src/main/resources/org/ajax4jsf/messages.properties
===================================================================
---
branches/enterprise/3.3.X/framework/impl/src/main/resources/org/ajax4jsf/messages.properties 2010-04-29
15:54:40 UTC (rev 16846)
+++
branches/enterprise/3.3.X/framework/impl/src/main/resources/org/ajax4jsf/messages.properties 2010-04-29
17:16:49 UTC (rev 16847)
@@ -259,4 +259,5 @@
INVALID_VALUE="Component {0} has invalid value expression {1}"
DATASCROLLER_PAGE_MISSING=Datascroller {0}: The requested page #{1} isn''t found
in the model containing {2} pages. Paging is reset to page #{3}
DATASCROLLER_PAGES_DIFFERENT=Datascroller components attached to component\: {0} have
different values of ''page'' attribute \: {1}
-COMPONENT_CONVERSION_ERROR=Component {0}\: conversion error occurred for value {1}
\ No newline at end of file
+COMPONENT_CONVERSION_ERROR=Component {0}\: conversion error occurred for value {1}
+JAVASCRIPT_CIRCULAR_REFERENCE=Circular reference occurred during serialization of {0}
object to JavaScript. Such references are not currently supported and will be nullified.
\ No newline at end of file