1
1
package play .modules .swagger ;
2
2
3
3
import com .fasterxml .jackson .databind .JavaType ;
4
- import com .fasterxml .jackson .databind .type .TypeFactory ;
5
4
import io .swagger .annotations .*;
6
5
import io .swagger .annotations .Info ;
7
6
import io .swagger .converter .ModelConverters ;
13
12
import io .swagger .models .parameters .Parameter ;
14
13
import io .swagger .models .properties .*;
15
14
import io .swagger .util .BaseReaderUtils ;
15
+ import io .swagger .util .Json ;
16
16
import io .swagger .util .ParameterProcessor ;
17
17
import io .swagger .util .PrimitiveType ;
18
18
import io .swagger .util .ReflectionUtils ;
19
19
import org .apache .commons .lang3 .StringUtils ;
20
20
import play .Logger ;
21
21
import play .modules .swagger .util .CrossUtil ;
22
22
import play .routes .compiler .*;
23
+ import scala .Option ;
23
24
24
25
import java .lang .annotation .Annotation ;
25
26
import java .lang .reflect .Method ;
26
27
import java .lang .reflect .Type ;
27
28
import java .util .*;
29
+ import java .util .regex .Matcher ;
30
+ import java .util .regex .Pattern ;
28
31
29
32
public class PlayReader {
30
33
@@ -192,7 +195,7 @@ private Swagger read(Class<?> cls, boolean readHidden) {
192
195
}
193
196
path .set (httpMethod , operation );
194
197
try {
195
- readImplicitParameters (method , operation );
198
+ readImplicitParameters (method , operation , cls );
196
199
} catch (Exception e ) {
197
200
throw e ;
198
201
}
@@ -350,19 +353,19 @@ protected void readInfoConfig(SwaggerDefinition config) {
350
353
info .getVendorExtensions ().putAll (BaseReaderUtils .parseExtensions (infoConfig .extensions ()));
351
354
}
352
355
353
- private void readImplicitParameters (Method method , Operation operation ) {
356
+ private void readImplicitParameters (Method method , Operation operation , Class <?> cls ) {
354
357
ApiImplicitParams implicitParams = method .getAnnotation (ApiImplicitParams .class );
355
358
if (implicitParams != null && implicitParams .value ().length > 0 ) {
356
359
for (ApiImplicitParam param : implicitParams .value ()) {
357
- Parameter p = readImplicitParam (param );
360
+ Parameter p = readImplicitParam (param , cls );
358
361
if (p != null ) {
359
362
operation .addParameter (p );
360
363
}
361
364
}
362
365
}
363
366
}
364
367
365
- protected io .swagger .models .parameters .Parameter readImplicitParam (ApiImplicitParam param ) {
368
+ protected io .swagger .models .parameters .Parameter readImplicitParam (ApiImplicitParam param , Class <?> cls ) {
366
369
final Parameter p ;
367
370
if (param .paramType ().equalsIgnoreCase ("path" )) {
368
371
p = new PathParameter ();
@@ -380,18 +383,31 @@ protected io.swagger.models.parameters.Parameter readImplicitParam(ApiImplicitPa
380
383
}
381
384
Type type = null ;
382
385
// Swagger ReflectionUtils can't handle file or array datatype
383
- if (!"file" .equalsIgnoreCase (param .dataType ()) && !"array" .equalsIgnoreCase (param .dataType ())){
384
- type = typeFromString (param .dataType ());
386
+ if (!"" .equalsIgnoreCase (param .dataType ()) && !"file" .equalsIgnoreCase (param .dataType ()) && !"array" .equalsIgnoreCase (param .dataType ())){
387
+ type = typeFromString (param .dataType (), cls );
388
+
385
389
}
386
- return ParameterProcessor .applyAnnotations (getSwagger (), p , type == null ? String .class : type , Collections .singletonList (param ));
390
+ Parameter result = ParameterProcessor .applyAnnotations (getSwagger (), p , type == null ? String .class : type , Collections .singletonList (param ));
391
+
392
+ if (result instanceof AbstractSerializableParameter && type != null ) {
393
+ Property schema = createProperty (type );
394
+ ((AbstractSerializableParameter )p ).setProperty (schema );
395
+ }
396
+
397
+ return result ;
398
+
387
399
}
388
400
389
- private static Type typeFromString (String type ) {
401
+ private static Type typeFromString (String type , Class <?> cls ) {
390
402
final PrimitiveType primitive = PrimitiveType .fromName (type );
391
403
if (primitive != null ) {
392
404
return primitive .getKeyClass ();
393
405
}
394
406
try {
407
+ Type routeType = getOptionTypeFromString (type , cls );
408
+
409
+ if (routeType != null ) return routeType ;
410
+
395
411
return Thread .currentThread ().getContextClassLoader ().loadClass (type );
396
412
} catch (Exception e ) {
397
413
Logger .error (String .format ("Failed to resolve '%s' into class" , type ), e );
@@ -522,37 +538,69 @@ private Operation parseMethod(Class<?> cls, Method method, Route route) {
522
538
return operation ;
523
539
}
524
540
525
- private Type getParamType (Class <?> cls , Method method , String simpleTypeName ) {
526
- Type [] genericParameterTypes = method .getGenericParameterTypes ();
527
- for (Type genericParameterType : genericParameterTypes ) {
528
- final Type type = TypeFactory .defaultInstance ().constructType (genericParameterType , cls );
529
- final Class <?> paramClass = ((JavaType ) type ).getRawClass ();
530
- if (simpleTypeName .equalsIgnoreCase (paramClass .getSimpleName ())) {
531
- return type ;
532
- }
533
- if (simpleTypeName .equalsIgnoreCase (paramClass .getName ())) {
534
- return type ;
541
+
542
+ final static class OptionTypeResolver {
543
+ private Option <Integer > optionTypeInt ;
544
+ private Option <Long > optionTypeLong ;
545
+ private Option <Byte > optionTypeByte ;
546
+ private Option <Boolean > optionTypeBoolean ;
547
+ private Option <Character > optionTypeChar ;
548
+ private Option <Float > optionTypeFloat ;
549
+ private Option <Double > optionTypeDouble ;
550
+ private Option <Short > optionTypeShort ;
551
+
552
+ static Type resolveOptionType (String innerType , Class <?> cls ) {
553
+ try {
554
+ return Json .mapper ().getTypeFactory ().constructType (
555
+ OptionTypeResolver .class .getDeclaredField ("optionType" + innerType ).getGenericType (), cls );
556
+ } catch (NoSuchFieldException e ) {
557
+ return null ;
535
558
}
536
559
}
537
- return null ;
538
560
}
539
561
540
- private List <Annotation > getParamAnnotations (Class <?> cls , Type [] genericParameterTypes , Annotation [][] paramAnnotations , String simpleTypeName , int fieldPosition ) {
541
- Type type = TypeFactory .defaultInstance ().constructType (genericParameterTypes [fieldPosition ], cls );
542
- Class <?> paramClass = ((JavaType )type ).getRawClass ();
543
- if (simpleTypeName .equalsIgnoreCase (paramClass .getSimpleName ())) {
544
- return Arrays .asList (paramAnnotations [fieldPosition ]);
562
+ private static Type getOptionTypeFromString (String simpleTypeName , Class <?> cls ) {
563
+
564
+ if (simpleTypeName == null ) return null ;
565
+ String regex = "(Option|scala\\ .Option)\\ s*\\ [\\ s*(Int|Long|Float|Double|Byte|Short|Char|Boolean)\\ s*\\ ]\\ s*$" ;
566
+
567
+ Pattern pattern = Pattern .compile (regex );
568
+ Matcher matcher = pattern .matcher (simpleTypeName );
569
+ if (matcher .find ())
570
+ {
571
+ String enhancedType = matcher .group (2 );
572
+ return OptionTypeResolver .resolveOptionType (enhancedType , cls );
573
+ } else {
574
+ return null ;
575
+ }
576
+ }
577
+ private Type getParamType (Class <?> cls , Method method , String simpleTypeName , int position ) {
578
+
579
+ try {
580
+ Type type = getOptionTypeFromString (simpleTypeName , cls );
581
+ if (type != null ) return type ;
582
+
583
+ Type [] genericParameterTypes = method .getGenericParameterTypes ();
584
+ return Json .mapper ().getTypeFactory ().constructType (genericParameterTypes [position ], cls );
585
+ } catch (Exception e ) {
586
+ Logger .error (String .format ("Exception getting parameter type for method %s, param %s at position %d" ), e );
587
+ return null ;
545
588
}
546
- if (simpleTypeName .equalsIgnoreCase (paramClass .getName ())) {
589
+
590
+ }
591
+
592
+ private List <Annotation > getParamAnnotations (Class <?> cls , Type [] genericParameterTypes , Annotation [][] paramAnnotations , String simpleTypeName , int fieldPosition ) {
593
+ try {
547
594
return Arrays .asList (paramAnnotations [fieldPosition ]);
595
+ } catch (Exception e ) {
596
+ Logger .error (String .format ("Exception getting parameter type for method %s, param %s at position %d" ), e );
597
+ return null ;
548
598
}
549
- return null ;
550
599
}
551
600
552
601
private List <Annotation > getParamAnnotations (Class <?> cls , Method method , String simpleTypeName , int fieldPosition ) {
553
602
Type [] genericParameterTypes = method .getGenericParameterTypes ();
554
603
Annotation [][] paramAnnotations = method .getParameterAnnotations ();
555
-
556
604
List <Annotation > annotations = getParamAnnotations (cls , genericParameterTypes , paramAnnotations , simpleTypeName , fieldPosition );
557
605
if (annotations != null ) {
558
606
return annotations ;
@@ -587,7 +635,7 @@ private List<Parameter> getParameters(Class<?> cls, Method method, Route route)
587
635
if (def .startsWith ("\" " ) && def .endsWith ("\" " )){
588
636
def = def .substring (1 ,def .length ()-1 );
589
637
}
590
- Type type = getParamType (cls , method , p .typeName ());
638
+ Type type = getParamType (cls , method , p .typeName (), fieldPosition );
591
639
Property schema = createProperty (type );
592
640
if (route .path ().has (p .name ())) {
593
641
// it's a path param
@@ -602,9 +650,7 @@ private List<Parameter> getParameters(Class<?> cls, Method method, Route route)
602
650
}
603
651
parameter .setName (p .name ());
604
652
List <Annotation > annotations = getParamAnnotations (cls , method , p .typeName (), fieldPosition );
605
-
606
653
ParameterProcessor .applyAnnotations (getSwagger (), parameter , type , annotations );
607
-
608
654
parameters .add (parameter );
609
655
fieldPosition ++;
610
656
}
@@ -623,15 +669,15 @@ private static Set<Scheme> parseSchemes(String schemes) {
623
669
}
624
670
625
671
private static boolean isVoid (Type type ) {
626
- final Class <?> cls = TypeFactory . defaultInstance ().constructType (type ).getRawClass ();
672
+ final Class <?> cls = Json . mapper (). getTypeFactory ().constructType (type ).getRawClass ();
627
673
return Void .class .isAssignableFrom (cls ) || Void .TYPE .isAssignableFrom (cls );
628
674
}
629
675
630
676
private static boolean isValidResponse (Type type ) {
631
677
if (type == null ) {
632
678
return false ;
633
679
}
634
- final JavaType javaType = TypeFactory . defaultInstance ().constructType (type );
680
+ final JavaType javaType = Json . mapper (). getTypeFactory ().constructType (type );
635
681
if (isVoid (javaType )) {
636
682
return false ;
637
683
}
0 commit comments