juneau-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamesbog...@apache.org
Subject incubator-juneau git commit: DynaBean support
Date Thu, 04 May 2017 16:56:07 GMT
Repository: incubator-juneau
Updated Branches:
  refs/heads/master ef55eca75 -> f8ee48642


DynaBean support

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

Branch: refs/heads/master
Commit: f8ee48642e65ab13e9be483b02e6035e975b84e1
Parents: ef55eca
Author: JamesBognar <jamesbognar@apache.org>
Authored: Thu May 4 12:56:03 2017 -0400
Committer: JamesBognar <jamesbognar@apache.org>
Committed: Thu May 4 12:56:03 2017 -0400

----------------------------------------------------------------------
 .../org/apache/juneau/DynaBeanComboTest.java    | 252 ++++++++++++++++++-
 .../main/java/org/apache/juneau/BeanMap.java    |   8 +-
 .../main/java/org/apache/juneau/BeanMeta.java   |  27 +-
 .../org/apache/juneau/BeanPropertyMeta.java     |  18 +-
 .../org/apache/juneau/BeanPropertyValue.java    |   7 +-
 .../apache/juneau/annotation/BeanProperty.java  |  69 +++++
 juneau-core/src/main/javadoc/overview.html      |   3 +-
 7 files changed, 355 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f8ee4864/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java b/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java
index bb9ac79..3be3753 100644
--- a/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/DynaBeanComboTest.java
@@ -19,6 +19,8 @@ import java.util.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.serializer.*;
+import org.apache.juneau.transforms.*;
+import org.junit.*;
 import org.junit.runner.*;
 import org.junit.runners.*;
 
@@ -33,10 +35,10 @@ public class DynaBeanComboTest extends ComboTest {
 	public static Collection<Object[]> getParameters() {
 		return Arrays.asList(new Object[][] {
 			{ 	/* 0 */
-				new ComboInput<A>(
-					"A",
-					A.class,
-					new A().init(),
+				new ComboInput<BeanWithDynaField>(
+					"BeanWithDynaField",
+					BeanWithDynaField.class,
+					new BeanWithDynaField().init(),
 					/* Json */		"{f1:1,f2a:'a',f2b:'b',f3:3}",
 					/* JsonT */		"{f1:1,f2a:'a',f2b:'b',f3:3}",
 					/* JsonR */		"{\n\tf1: 1,\n\tf2a: 'a',\n\tf2b: 'b',\n\tf3: 3\n}",
@@ -60,8 +62,142 @@ public class DynaBeanComboTest extends ComboTest {
 					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <jp:f1>1</jp:f1>\n
   <jp:f2a>a</jp:f2a>\n    <jp:f2b>b</jp:f2b>\n    <jp:f3>3</jp:f3>\n
 </rdf:Description>\n</rdf:RDF>\n"
 				)
 				{
-					public void verify(A o) {
-						assertType(A.class, o);
+					public void verify(BeanWithDynaField o) {
+						assertType(BeanWithDynaField.class, o);
+					}
+				}
+			},
+			{ 	/* 1 */
+				new ComboInput<BeanWithDynaMethods>(
+					"BeanWithDynaMethods",
+					BeanWithDynaMethods.class,
+					new BeanWithDynaMethods().init(),
+					/* Json */		"{f1:1,f2a:'a',f2b:'b',f3:3}",
+					/* JsonT */		"{f1:1,f2a:'a',f2b:'b',f3:3}",
+					/* JsonR */		"{\n\tf1: 1,\n\tf2a: 'a',\n\tf2b: 'b',\n\tf3: 3\n}",
+					/* Xml */		"<object><f1>1</f1><f2a>a</f2a><f2b>b</f2b><f3>3</f3></object>",
+					/* XmlT */		"<object><f1>1</f1><f2a>a</f2a><f2b>b</f2b><f3>3</f3></object>",
+					/* XmlR */		"<object>\n\t<f1>1</f1>\n\t<f2a>a</f2a>\n\t<f2b>b</f2b>\n\t<f3>3</f3>\n</object>\n",
+					/* XmlNs */		"<object><f1>1</f1><f2a>a</f2a><f2b>b</f2b><f3>3</f3></object>",
+					/* Html */		"<table><tr><td>f1</td><td>1</td></tr><tr><td>f2a</td><td>a</td></tr><tr><td>f2b</td><td>b</td></tr><tr><td>f3</td><td>3</td></tr></table>",
+					/* HtmlT */		"<table><tr><td>f1</td><td>1</td></tr><tr><td>f2a</td><td>a</td></tr><tr><td>f2b</td><td>b</td></tr><tr><td>f3</td><td>3</td></tr></table>",
+					/* HtmlR */		"<table>\n\t<tr>\n\t\t<td>f1</td>\n\t\t<td>1</td>\n\t</tr>\n\t<tr>\n\t\t<td>f2a</td>\n\t\t<td>a</td>\n\t</tr>\n\t<tr>\n\t\t<td>f2b</td>\n\t\t<td>b</td>\n\t</tr>\n\t<tr>\n\t\t<td>f3</td>\n\t\t<td>3</td>\n\t</tr>\n</table>\n",
+					/* Uon */		"(f1=1,f2a=a,f2b=b,f3=3)",
+					/* UonT */		"(f1=1,f2a=a,f2b=b,f3=3)",
+					/* UonR */		"(\n\tf1=1,\n\tf2a=a,\n\tf2b=b,\n\tf3=3\n)",
+					/* UrlEnc */	"f1=1&f2a=a&f2b=b&f3=3",
+					/* UrlEncT */	"f1=1&f2a=a&f2b=b&f3=3",
+					/* UrlEncR */	"f1=1\n&f2a=a\n&f2b=b\n&f3=3",
+					/* MsgPack */	"84A2663101A3663261A161A3663262A162A2663303",
+					/* MsgPackT */	"84A2663101A3663261A161A3663262A162A2663303",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<jp:f1>1</jp:f1>\n<jp:f2a>a</jp:f2a>\n<jp:f2b>b</jp:f2b>\n<jp:f3>3</jp:f3>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<jp:f1>1</jp:f1>\n<jp:f2a>a</jp:f2a>\n<jp:f2b>b</jp:f2b>\n<jp:f3>3</jp:f3>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <jp:f1>1</jp:f1>\n
   <jp:f2a>a</jp:f2a>\n    <jp:f2b>b</jp:f2b>\n    <jp:f3>3</jp:f3>\n
 </rdf:Description>\n</rdf:RDF>\n"
+				)
+				{
+					public void verify(BeanWithDynaMethods o) {
+						assertType(BeanWithDynaMethods.class, o);
+						Assert.assertTrue(o.setterCalled);
+					}
+				}
+			},
+			{ 	/* 2 */
+				new ComboInput<BeanWithDynaGetterOnly>(
+					"BeanWithDynaGetterOnly",
+					BeanWithDynaGetterOnly.class,
+					new BeanWithDynaGetterOnly().init(),
+					/* Json */		"{f1:1,f2a:'a',f2b:'b',f3:3}",
+					/* JsonT */		"{f1:1,f2a:'a',f2b:'b',f3:3}",
+					/* JsonR */		"{\n\tf1: 1,\n\tf2a: 'a',\n\tf2b: 'b',\n\tf3: 3\n}",
+					/* Xml */		"<object><f1>1</f1><f2a>a</f2a><f2b>b</f2b><f3>3</f3></object>",
+					/* XmlT */		"<object><f1>1</f1><f2a>a</f2a><f2b>b</f2b><f3>3</f3></object>",
+					/* XmlR */		"<object>\n\t<f1>1</f1>\n\t<f2a>a</f2a>\n\t<f2b>b</f2b>\n\t<f3>3</f3>\n</object>\n",
+					/* XmlNs */		"<object><f1>1</f1><f2a>a</f2a><f2b>b</f2b><f3>3</f3></object>",
+					/* Html */		"<table><tr><td>f1</td><td>1</td></tr><tr><td>f2a</td><td>a</td></tr><tr><td>f2b</td><td>b</td></tr><tr><td>f3</td><td>3</td></tr></table>",
+					/* HtmlT */		"<table><tr><td>f1</td><td>1</td></tr><tr><td>f2a</td><td>a</td></tr><tr><td>f2b</td><td>b</td></tr><tr><td>f3</td><td>3</td></tr></table>",
+					/* HtmlR */		"<table>\n\t<tr>\n\t\t<td>f1</td>\n\t\t<td>1</td>\n\t</tr>\n\t<tr>\n\t\t<td>f2a</td>\n\t\t<td>a</td>\n\t</tr>\n\t<tr>\n\t\t<td>f2b</td>\n\t\t<td>b</td>\n\t</tr>\n\t<tr>\n\t\t<td>f3</td>\n\t\t<td>3</td>\n\t</tr>\n</table>\n",
+					/* Uon */		"(f1=1,f2a=a,f2b=b,f3=3)",
+					/* UonT */		"(f1=1,f2a=a,f2b=b,f3=3)",
+					/* UonR */		"(\n\tf1=1,\n\tf2a=a,\n\tf2b=b,\n\tf3=3\n)",
+					/* UrlEnc */	"f1=1&f2a=a&f2b=b&f3=3",
+					/* UrlEncT */	"f1=1&f2a=a&f2b=b&f3=3",
+					/* UrlEncR */	"f1=1\n&f2a=a\n&f2b=b\n&f3=3",
+					/* MsgPack */	"84A2663101A3663261A161A3663262A162A2663303",
+					/* MsgPackT */	"84A2663101A3663261A161A3663262A162A2663303",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<jp:f1>1</jp:f1>\n<jp:f2a>a</jp:f2a>\n<jp:f2b>b</jp:f2b>\n<jp:f3>3</jp:f3>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<jp:f1>1</jp:f1>\n<jp:f2a>a</jp:f2a>\n<jp:f2b>b</jp:f2b>\n<jp:f3>3</jp:f3>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <jp:f1>1</jp:f1>\n
   <jp:f2a>a</jp:f2a>\n    <jp:f2b>b</jp:f2b>\n    <jp:f3>3</jp:f3>\n
 </rdf:Description>\n</rdf:RDF>\n"
+				)
+				{
+					public void verify(BeanWithDynaGetterOnly o) {
+						assertType(BeanWithDynaGetterOnly.class, o);
+					}
+				}
+			},
+			{ 	/* 3 */
+				new ComboInput<BeanWithDynaFieldSwapped>(
+					"BeanWithDynaFieldSwapped",
+					BeanWithDynaFieldSwapped.class,
+					new BeanWithDynaFieldSwapped().init(),
+					/* Json */		"{f1a:'1901-03-03T18:11:12Z'}",
+					/* JsonT */		"{f1a:'1901-03-03T18:11:12Z'}",
+					/* JsonR */		"{\n\tf1a: '1901-03-03T18:11:12Z'\n}",
+					/* Xml */		"<object><f1a>1901-03-03T18:11:12Z</f1a></object>",
+					/* XmlT */		"<object><f1a>1901-03-03T18:11:12Z</f1a></object>",
+					/* XmlR */		"<object>\n\t<f1a>1901-03-03T18:11:12Z</f1a>\n</object>\n",
+					/* XmlNs */		"<object><f1a>1901-03-03T18:11:12Z</f1a></object>",
+					/* Html */		"<table><tr><td>f1a</td><td>1901-03-03T18:11:12Z</td></tr></table>",
+					/* HtmlT */		"<table><tr><td>f1a</td><td>1901-03-03T18:11:12Z</td></tr></table>",
+					/* HtmlR */		"<table>\n\t<tr>\n\t\t<td>f1a</td>\n\t\t<td>1901-03-03T18:11:12Z</td>\n\t</tr>\n</table>\n",
+					/* Uon */		"(f1a=1901-03-03T18:11:12Z)",
+					/* UonT */		"(f1a=1901-03-03T18:11:12Z)",
+					/* UonR */		"(\n\tf1a=1901-03-03T18:11:12Z\n)",
+					/* UrlEnc */	"f1a=1901-03-03T18:11:12Z",
+					/* UrlEncT */	"f1a=1901-03-03T18:11:12Z",
+					/* UrlEncR */	"f1a=1901-03-03T18:11:12Z",
+					/* MsgPack */	"81A3663161B4313930312D30332D30335431383A31313A31325A",
+					/* MsgPackT */	"81A3663161B4313930312D30332D30335431383A31313A31325A",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<jp:f1a>1901-03-03T18:11:12Z</jp:f1a>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<jp:f1a>1901-03-03T18:11:12Z</jp:f1a>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <jp:f1a>1901-03-03T18:11:12Z</jp:f1a>\n
 </rdf:Description>\n</rdf:RDF>\n"
+				)
+				{
+					public void verify(BeanWithDynaFieldSwapped o) {
+						assertType(BeanWithDynaFieldSwapped.class, o);
+						assertType(Calendar.class, o.f1.get("f1a"));
+					}
+				}
+			},
+			{ 	/* 4 */
+				new ComboInput<BeanWithDynaFieldStringList>(
+					"BeanWithDynaFieldStringList",
+					BeanWithDynaFieldStringList.class,
+					new BeanWithDynaFieldStringList().init(),
+					/* Json */		"{f1a:['foo','bar']}",
+					/* JsonT */		"{f1a:['foo','bar']}",
+					/* JsonR */		"{\n\tf1a: [\n\t\t'foo',\n\t\t'bar'\n\t]\n}",
+					/* Xml */		"<object><f1a><string>foo</string><string>bar</string></f1a></object>",
+					/* XmlT */		"<object><f1a><string>foo</string><string>bar</string></f1a></object>",
+					/* XmlR */		"<object>\n\t<f1a>\n\t\t<string>foo</string>\n\t\t<string>bar</string>\n\t</f1a>\n</object>\n",
+					/* XmlNs */		"<object><f1a><string>foo</string><string>bar</string></f1a></object>",
+					/* Html */		"<table><tr><td>f1a</td><td><ul><li>foo</li><li>bar</li></ul></td></tr></table>",
+					/* HtmlT */		"<table><tr><td>f1a</td><td><ul><li>foo</li><li>bar</li></ul></td></tr></table>",
+					/* HtmlR */		"<table>\n\t<tr>\n\t\t<td>f1a</td>\n\t\t<td>\n\t\t\t<ul>\n\t\t\t\t<li>foo</li>\n\t\t\t\t<li>bar</li>\n\t\t\t</ul>\n\t\t</td>\n\t</tr>\n</table>\n",
+					/* Uon */		"(f1a=@(foo,bar))",
+					/* UonT */		"(f1a=@(foo,bar))",
+					/* UonR */		"(\n\tf1a=@(\n\t\tfoo,\n\t\tbar\n\t)\n)",
+					/* UrlEnc */	"f1a=@(foo,bar)",
+					/* UrlEncT */	"f1a=@(foo,bar)",
+					/* UrlEncR */	"f1a=@(\n\tfoo,\n\tbar\n)",
+					/* MsgPack */	"81A366316192A3666F6FA3626172",
+					/* MsgPackT */	"81A366316192A3666F6FA3626172",
+					/* RdfXml */	"<rdf:RDF>\n<rdf:Description>\n<jp:f1a>\n<rdf:Seq>\n<rdf:li>foo</rdf:li>\n<rdf:li>bar</rdf:li>\n</rdf:Seq>\n</jp:f1a>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlT */	"<rdf:RDF>\n<rdf:Description>\n<jp:f1a>\n<rdf:Seq>\n<rdf:li>foo</rdf:li>\n<rdf:li>bar</rdf:li>\n</rdf:Seq>\n</jp:f1a>\n</rdf:Description>\n</rdf:RDF>\n",
+					/* RdfXmlR */	"<rdf:RDF>\n  <rdf:Description>\n    <jp:f1a>\n    
 <rdf:Seq>\n        <rdf:li>foo</rdf:li>\n        <rdf:li>bar</rdf:li>\n
     </rdf:Seq>\n    </jp:f1a>\n  </rdf:Description>\n</rdf:RDF>\n"
+				)
+				{
+					public void verify(BeanWithDynaFieldStringList o) {
+						assertType(BeanWithDynaFieldStringList.class, o);
 					}
 				}
 			},
@@ -82,17 +218,117 @@ public class DynaBeanComboTest extends ComboTest {
 		return p.builder().build();
 	}
 	
-	public static class A {
+	@Bean(sort=true)
+	public static class BeanWithDynaField {
 		public int f1;
 		@BeanProperty(name="*")
 		public Map<String,Object> f2 = new LinkedHashMap<String,Object>();
 		public int f3;
 	
-		public A init() {
+		public BeanWithDynaField init() {
+			this.f1 = 1;
+			this.f2 = new ObjectMap().append("f2a", "a").append("f2b", "b");
+			this.f3 = 3;
+			return this;
+		}
+	}
+
+	@Bean(sort=true)
+	public static class BeanWithDynaMethods {
+
+		private int f1, f3;
+		private Map<String,Object> f2 = new LinkedHashMap<String,Object>();
+		private boolean setterCalled = false;
+	
+		public int getF1() {
+			return f1;
+		}
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+		public int getF3() {
+			return f3;
+		}
+		public void setF3(int f3) {
+			this.f3 = f3;
+		}
+
+		@BeanProperty(name="*")
+		public Map<String, Object> xxx() {
+			return f2;
+		}
+
+		@BeanProperty(name="*")
+		public void yyy(String name, Object o) {
+			setterCalled = true;
+			this.f2.put(name, o);
+		}
+
+		public BeanWithDynaMethods init() {
+			this.f1 = 1;
+			this.f2 = new ObjectMap().append("f2a", "a").append("f2b", "b");
+			this.f3 = 3;
+			return this;
+		}
+	}
+
+	@Bean(sort=true)
+	public static class BeanWithDynaGetterOnly {
+
+		private int f1, f3;
+		private Map<String,Object> f2 = new LinkedHashMap<String,Object>();
+	
+		public int getF1() {
+			return f1;
+		}
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+		public int getF3() {
+			return f3;
+		}
+		public void setF3(int f3) {
+			this.f3 = f3;
+		}
+
+		@BeanProperty(name="*")
+		public Map<String, Object> xxx() {
+			return f2;
+		}
+
+		public BeanWithDynaGetterOnly init() {
 			this.f1 = 1;
 			this.f2 = new ObjectMap().append("f2a", "a").append("f2b", "b");
 			this.f3 = 3;
 			return this;
 		}
 	}
+	
+	private static Calendar singleDate = new GregorianCalendar(TimeZone.getTimeZone("PST"));
+	static {
+		singleDate.setTimeInMillis(0);
+		singleDate.set(1901, 2, 3, 10, 11, 12);
+	}
+
+	@Bean(sort=true)
+	public static class BeanWithDynaFieldSwapped {
+		@BeanProperty(name="*", swap=CalendarSwap.ISO8601DTZ.class)
+		public Map<String,Calendar> f1 = new LinkedHashMap<String,Calendar>();
+	
+		public BeanWithDynaFieldSwapped init() {
+			this.f1.put("f1a", singleDate);
+			return this;
+		}
+	}
+
+	@Bean(sort=true)
+	public static class BeanWithDynaFieldStringList {
+		@BeanProperty(name="*")
+		public Map<String,List<String>> f1 = new LinkedHashMap<String,List<String>>();
+	
+		public BeanWithDynaFieldStringList init() {
+			this.f1.put("f1a", Arrays.asList(new String[]{"foo","bar"}));
+			return this;
+		}
+	}
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f8ee4864/juneau-core/src/main/java/org/apache/juneau/BeanMap.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMap.java b/juneau-core/src/main/java/org/apache/juneau/BeanMap.java
index 85e1670..e6a1566 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanMap.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanMap.java
@@ -417,10 +417,10 @@ public class BeanMap<T> extends AbstractMap<String,Object>
implements Delegate<T
 		for (BeanPropertyMeta bpm : properties) {
 			try {
 				if (bpm.isDyna()) {
-					for (Map.Entry<String,Object> e : bpm.getDynaMap(bean).entrySet()) {
-						Object val = e.getValue();
+					for (String pName : bpm.getDynaMap(bean).keySet()) {
+						Object val = bpm.get(this, pName);
 						if (val != null || ! ignoreNulls)
-							l.add(new BeanPropertyValue(bpm, e.getKey(), val, null));
+							l.add(new BeanPropertyValue(bpm, pName, val, null));
 					}
 				} else {
 					Object val = bpm.get(this, null);
@@ -434,6 +434,8 @@ public class BeanMap<T> extends AbstractMap<String,Object>
implements Delegate<T
 				l.add(new BeanPropertyValue(bpm, bpm.getName(), null, t));
 			}
 		}
+		if (meta.sortProperties && meta.dynaProperty != null)
+			Collections.sort(l);
 		return l;
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f8ee4864/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
index bfa5246..6ca660c 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
@@ -93,6 +93,7 @@ public class BeanMeta<T> {
 	private final String dictionaryName;                                // The @Bean.typeName()
annotation defined on this bean class.
 	final String notABeanReason;                                        // Readable string explaining
why this class wasn't a bean.
 	final BeanRegistry beanRegistry;
+	final boolean sortProperties;
 
 	/**
 	 * Constructor.
@@ -123,6 +124,7 @@ public class BeanMeta<T> {
 		this.beanRegistry = b.beanRegistry;
 		this.typePropertyName = b.typePropertyName;
 		this.typeProperty = new BeanPropertyMeta.Builder(this, typePropertyName, ctx.string(),
beanRegistry).build();
+		this.sortProperties = b.sortProperties;
 	}
 
 	private static final class Builder<T> {
@@ -142,6 +144,7 @@ public class BeanMeta<T> {
 		PropertyNamer propertyNamer;
 		BeanRegistry beanRegistry;
 		String dictionaryName, typePropertyName;
+		boolean sortProperties;
 
 		private Builder(ClassMeta<T> classMeta, BeanContext ctx, BeanFilter beanFilter, String[]
pNames) {
 			this.classMeta = classMeta;
@@ -341,7 +344,7 @@ public class BeanMeta<T> {
 				if (beanFilter == null && ctx.beansRequireSomeProperties && normalProps.size()
== 0)
 					return "No properties detected on bean class";
 
-				boolean sortProperties = (ctx.sortProperties || (beanFilter != null && beanFilter.isSortProperties()))
&& fixedBeanProps.isEmpty();
+				sortProperties = (ctx.sortProperties || (beanFilter != null && beanFilter.isSortProperties()))
&& fixedBeanProps.isEmpty();
 
 				properties = sortProperties ? new TreeMap<String,BeanPropertyMeta>() : new LinkedHashMap<String,BeanPropertyMeta>();
 
@@ -493,6 +496,10 @@ public class BeanMeta<T> {
 			if (b == null)
 				return false;
 
+			// Don't do further validation if this is the "*" bean property.
+			if ("*".equals(b.name))
+				return true;
+
 			// Get the bean property type from the getter/field.
 			Class<?> pt = null;
 			if (b.getter != null)
@@ -551,6 +558,7 @@ public class BeanMeta<T> {
 				Class<?> rt = m.getReturnType();
 				boolean isGetter = false, isSetter = false;
 				BeanProperty bp = m.getAnnotation(BeanProperty.class);
+				String bpName = bp == null ? "" : bp.name();
 				if (pt.length == 0) {
 					if (n.startsWith("get") && (! rt.equals(Void.TYPE))) {
 						isGetter = true;
@@ -558,23 +566,28 @@ public class BeanMeta<T> {
 					} else if (n.startsWith("is") && (rt.equals(Boolean.TYPE) || rt.equals(Boolean.class)))
{
 						isGetter = true;
 						n = n.substring(2);
-					} else if (bp != null && ! bp.name().isEmpty()) {
+					} else if (! bpName.isEmpty()) {
 						isGetter = true;
-						n = bp.name();
+						n = bpName;
 					}
 				} else if (pt.length == 1) {
 					if (n.startsWith("set") && (isParentClass(rt, c) || rt.equals(Void.TYPE))) {
 						isSetter = true;
 						n = n.substring(3);
-					} else if (bp != null && ! bp.name().isEmpty()) {
+					} else if (! bpName.isEmpty()) {
+						isSetter = true;
+						n = bpName;
+					}
+				} else if (pt.length == 2) {
+					if ("*".equals(bpName)) {
 						isSetter = true;
-						n = bp.name();
+						n = bpName;
 					}
 				}
 				n = pn.getPropertyName(n);
 				if (isGetter || isSetter) {
-					if (bp != null && ! bp.name().equals("")) {
-						n = bp.name();
+					if (! bpName.isEmpty()) {
+						n = bpName;
 						if (! fixedBeanProps.isEmpty())
 							if (! fixedBeanProps.contains(n))
 								throw new BeanRuntimeException(c, "Method property ''{0}'' identified in @BeanProperty,
but missing from @Bean", n);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f8ee4864/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
index ca20da7..409373a 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -166,11 +166,6 @@ public class BeanPropertyMeta {
 
 			isDyna = "*".equals(name);
 
-			if (isDyna)
-				rawTypeMeta = rawTypeMeta.getValueType();
-			if (rawTypeMeta == null)
-				return false;
-
 			// Do some annotation validation.
 			Class<?> c = rawTypeMeta.getInnerClass();
 			if (getter != null) {
@@ -187,9 +182,7 @@ public class BeanPropertyMeta {
 				if (pt.length != (isDyna ? 2 : 1))
 					return false;
 				if (isDyna) {
-					if (pt[0].equals(String.class))
-						return false;
-					if (! isParentClass(pt[1], c))
+					if (! pt[0].equals(String.class))
 						return false;
 				} else {
 					if (! isParentClass(pt[0], c))
@@ -206,6 +199,11 @@ public class BeanPropertyMeta {
 				}
 			}
 
+			if (isDyna)
+				rawTypeMeta = rawTypeMeta.getValueType();
+			if (rawTypeMeta == null)
+				return false;
+
 			if (typeMeta == null)
 				typeMeta = (swap != null ? swap.getSwapClassMeta(beanContext) : rawTypeMeta == null ?
beanContext.object() : rawTypeMeta.getSerializedClassMeta());
 			if (typeMeta == null)
@@ -497,7 +495,7 @@ public class BeanPropertyMeta {
 			boolean isMap = rawTypeMeta.isMap();
 			boolean isCollection = rawTypeMeta.isCollection();
 
-			if (field == null && setter == null && ! (isMap || isCollection)) {
+			if ((! isDyna) && field == null && setter == null && ! (isMap
|| isCollection)) {
 				if ((value == null && beanContext.ignoreUnknownNullBeanProperties) || beanContext.ignorePropertiesWithoutSetters)
 					return null;
 				throw new BeanRuntimeException(beanMeta.c, "Setter or public field not defined on property
''{0}''", name);
@@ -671,6 +669,8 @@ public class BeanPropertyMeta {
 			Map m = null;
 			if (field != null)
 				m = (Map<String,Object>)field.get(bean);
+			else if (getter != null)
+				m = (Map<String,Object>)getter.invoke(bean);
 			else
 				throw new BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}''
to object of type ''{2}'' because no setter is defined on this property, and the existing
property value is null", name, this.getClassMeta().getInnerClass().getName(), findClassName(val));
 			return (m == null ? null : m.put(pName, val));

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f8ee4864/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java
index de9d796..68abb37 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java
@@ -16,7 +16,7 @@ package org.apache.juneau;
  * Represents a simple bean property value and the meta-data associated with it.
  * <p>
  */
-public class BeanPropertyValue {
+public class BeanPropertyValue implements Comparable<BeanPropertyValue> {
 
 	private final BeanPropertyMeta pMeta;
 	private final String name;
@@ -77,4 +77,9 @@ public class BeanPropertyValue {
 	public final Throwable getThrown() {
 		return thrown;
 	}
+
+	@Override /* Comparable */
+	public int compareTo(BeanPropertyValue o) {
+		return name.compareTo(o.name);
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f8ee4864/juneau-core/src/main/java/org/apache/juneau/annotation/BeanProperty.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/annotation/BeanProperty.java b/juneau-core/src/main/java/org/apache/juneau/annotation/BeanProperty.java
index f934c9e..daf6ca1 100644
--- a/juneau-core/src/main/java/org/apache/juneau/annotation/BeanProperty.java
+++ b/juneau-core/src/main/java/org/apache/juneau/annotation/BeanProperty.java
@@ -54,6 +54,75 @@ public @interface BeanProperty {
 	 * <p>
 	 * If the {@link BeanContext#BEAN_beanFieldVisibility} setting on the bean context excludes
this field (e.g. the visibility
 	 * 	is set to PUBLIC, but the field is PROTECTED), this annotation can be used to force
the field to be identified as a property.
+	 * <p>
+	 * <h6 class='topic'>Dynamic beans</h6>
+	 * The bean property named <js>"*"</js> is the designated "dynamic property"
which allows for "extra" bean properties not otherwise defined.
+	 * This is similar in concept to the Jackson <ja>@JsonGetterAll</ja> and <ja>@JsonSetterAll</ja>
annotations.
+	 * The primary purpose is for backwards compatibility in parsing newer streams with addition
information into older beans.
+	 * <p>
+	 *	The following examples show how to define dynamic bean properties.
+	 * <p class='bcode'>
+	 * 	<jc>// Option #1 - A simple public Map field.
+	 * 	// The field name can be anything.</jc>
+	 * 	<jk>public class</jk> BeanWithDynaField {
+	 *
+	 * 		<ja>@BeanProperty</ja>(name=<js>"*"</js>)
+	 * 		<jk>public</jk> Map&lt;String,Object&gt; extraStuff = <jk>new</jk>
LinkedHashMap&lt;String,Object&gt;();
+	 * 	}
+	 *
+	 * 	<jc>// Option #2 - Getters and setters.
+	 * 	// Method names can be anything.
+	 * 	// Getter must return a Map with String keys.
+	 * 	// Setter must take in two arguments.</jc>
+	 * 	<jk>public class</jk> BeanWithDynaMethods {
+	 *
+	 * 		<ja>@BeanProperty</ja>(name=<js>"*"</js>)
+	 * 		<jk>public</jk> Map&lt;String,Object&gt; getMyExtraStuff() {
+	 * 			...
+	 * 		}
+	 *
+	 * 		<ja>@BeanProperty</ja>(name=<js>"*"</js>)
+	 * 		<jk>public void</jk> setAnExtraField(String name, Object value) {
+	 * 			...
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// Option #3 - Getter only.
+	 * 	// Properties will be added through the getter.</jc>
+	 * 	<jk>public class</jk> BeanWithDynaGetterOnly {
+	 *
+	 * 		<ja>@BeanProperty</ja>(name=<js>"*"</js>)
+	 * 		<jk>public</jk> Map&lt;String,Object&gt; getMyExtraStuff() {
+	 * 			...
+	 * 		}
+	 * 	}
+	 * </p>
+	 *	<p>
+	 *	Similar rules apply for value types and swaps.  The property values optionally can be
any serializable type
+	 *	or use swaps.
+	 * <p class='bcode'>
+	 * 	<jc>// A serializable type other than Object.</jc>
+	 * 	<jk>public class</jk> BeanWithDynaFieldWithListValues {
+	 *
+	 * 		<ja>@BeanProperty</ja>(name=<js>"*"</js>)
+	 * 		<jk>public</jk> Map&lt;String,List&lt;String&gt;&gt; getMyExtraStuff()
{
+	 * 			...
+	 * 		}
+	 * 	}
+	 *
+	 * 	<jc>// A swapped value.</jc>
+	 * 	<jk>public class</jk> BeanWithDynaFieldWithSwappedValues {
+	 *
+	 * 		<ja>@BeanProperty</ja>(name=<js>"*"</js>, swap=CalendarSwap.<jsf>ISO8601DTZ</jsf>.<jk>class</jk>)
+	 * 		<jk>public</jk> Map&lt;String,Calendar&gt; getMyExtraStuff() {
+	 * 			...
+	 * 		}
+	 * 	}
+	 * </p>
+	 * <p class='info'>
+	 * Note that if you're not interested in these additional properties, you can also use the
{@link BeanContext#BEAN_ignoreUnknownBeanProperties} setting
+	 * to ignore values that don't fit into existing properties.
+	 * </p>
 	 */
 	String name() default "";
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f8ee4864/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index 6cfe84b..96a8b5c 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -5819,6 +5819,7 @@
 				<li>{@link org.apache.juneau.http.AcceptEncoding}
 				<li>{@link org.apache.juneau.http.ContentType}
 			</ul>
+			<li>Support for dynamic beans.  See {@link org.apache.juneau.annotation.BeanProperty#name()
@BeanProperty.name()}.
 		</ul>
 
 		<h6 class='topic'>org.apache.juneau.rest</h6>
@@ -7573,7 +7574,7 @@
 			<li>New {@link org.apache.juneau.ClassMeta#isInstance(Object)} method.
 			<li>Performance improvements when using the {@link org.apache.juneau.BeanMap#add(String,Object)}
method.  
 				Array properties are stored in a temporary list cache until {@link org.apache.juneau.BeanMap#getBean()}
is called.
-			<li>New {@link org.apache.juneau.BeanPropertyMeta#add(BeanMap,Object)} method for
adding values to Collection and array properties.
+			<li>New <code><del>BeanPropertyMeta.add(BeanMap,Object)</del></code>
method for adding values to Collection and array properties.
 			<li>Config INI files now support keys with name <js>"*"</js>.
 		</ul>
 


Mime
View raw message