2525import java .util .List ;
2626import java .util .Map ;
2727import java .util .Map .Entry ;
28+ import java .util .function .Function ;
2829import java .util .stream .Collectors ;
2930
3031import javax .inject .Inject ;
3132
33+ import org .jooby .Err ;
3234import org .jooby .Mutant ;
3335import org .jooby .Parser ;
3436import org .jooby .Request ;
4648import com .google .common .reflect .Reflection ;
4749import com .google .inject .TypeLiteral ;
4850
51+ import javaslang .control .Try ;
52+
4953public class BeanParser implements Parser {
5054
55+ private Function <? super Throwable , Try <? extends Object >> MISSING = x -> {
56+ return x instanceof Err .Missing ? Try .success (null ) : Try .failure (x );
57+ };
58+
59+ private Function <? super Throwable , Try <? extends Object >> RETHROW = Try ::failure ;
60+
61+ private Function <? super Throwable , Try <? extends Object >> recoverMissing ;
62+
63+ public BeanParser (final boolean allowNulls ) {
64+ this .recoverMissing = allowNulls ? MISSING : RETHROW ;
65+ }
66+
5167 @ Override
5268 public Object parse (final TypeLiteral <?> type , final Context ctx ) throws Throwable {
5369 Class <?> beanType = type .getRawType ();
@@ -58,7 +74,7 @@ public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwab
5874 return ctx .ifparams (map -> {
5975 final Object bean ;
6076 if (beanType .isInterface ()) {
61- bean = newBeanInterface (ctx .require (Request .class ), beanType );
77+ bean = newBeanInterface (ctx .require (Request .class ), ctx . require ( Response . class ), beanType );
6278 } else {
6379 bean = newBean (ctx .require (Request .class ), ctx .require (Response .class ), map , beanType );
6480 }
@@ -85,17 +101,16 @@ private Object newBean(final Request req, final Response rsp,
85101 if (constructors .size () > 1 ) {
86102 return null ;
87103 }
88- final Object bean ;
89104 Constructor <?> constructor = constructors .get (0 );
90105 RequestParamProvider provider = new RequestParamProviderImpl (
91106 new RequestParamNameProviderImpl (classInfo ));
92107 List <RequestParam > parameters = provider .parameters (constructor );
93108 Object [] args = new Object [parameters .size ()];
94109 for (int i = 0 ; i < args .length ; i ++) {
95- args [i ] = parameters .get (i ). value ( req , rsp );
110+ args [i ] = value ( parameters .get (i ), req , rsp );
96111 }
97112 // inject args
98- bean = constructor .newInstance (args );
113+ final Object bean = constructor .newInstance (args );
99114
100115 // inject fields
101116 for (Entry <String , Mutant > param : params .entrySet ()) {
@@ -109,9 +124,7 @@ private Object newBean(final Request req, final Response rsp,
109124 int mods = field .getModifiers ();
110125 if (!Modifier .isFinal (mods ) && !Modifier .isStatic (mods ) && !Modifier .isTransient (mods )) {
111126 // get
112- RequestParam fparam = new RequestParam (field , pname , field .getGenericType ());
113- Object value = fparam .value (req , rsp );
114-
127+ Object value = value (new RequestParam (field , pname , field .getGenericType ()), req , rsp );
115128 // set
116129 field .setAccessible (true );
117130 field .set (root , value );
@@ -148,17 +161,24 @@ private Object seek(final Object bean, final List<String> path) throws Exception
148161 return it ;
149162 }
150163
151- private Object newBeanInterface (final Request req , final Class <?> beanType ) {
152-
164+ private Object newBeanInterface (final Request req , final Response rsp , final Class <?> beanType ) {
153165 return Reflection .newProxy (beanType , (proxy , method , args ) -> {
154166 StringBuilder name = new StringBuilder (method .getName ()
155167 .replace ("get" , "" )
156168 .replace ("is" , "" ));
157169 name .setCharAt (0 , Character .toLowerCase (name .charAt (0 )));
158- return req .param (name .toString ()).to (TypeLiteral .get (method .getGenericReturnType ()));
170+ return value (new RequestParam (method , name .toString (), method .getGenericReturnType ()), req ,
171+ rsp );
159172 });
160173 }
161174
175+ private Object value (final RequestParam param , final Request req , final Response rsp )
176+ throws Throwable {
177+ return Try .of (() -> param .value (req , rsp ))
178+ .recoverWith (recoverMissing )
179+ .getOrElseThrow (Function .identity ());
180+ }
181+
162182 private static List <String > name (final String name ) {
163183 return Splitter .on (new CharMatcher () {
164184 @ Override
0 commit comments