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 <[email protected]>
Authored: Thu May 4 12:56:03 2017 -0400
Committer: JamesBognar <[email protected]>
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>
 

Reply via email to