3434import java .util .Map ;
3535import java .util .Optional ;
3636import java .util .Set ;
37+ import java .util .function .BiConsumer ;
38+ import java .util .function .Function ;
3739import java .util .stream .Collectors ;
3840
3941import org .jooby .internal .DeferredExecution ;
4042import org .jooby .internal .RouteImpl ;
4143import org .jooby .internal .RouteMatcher ;
4244import org .jooby .internal .RoutePattern ;
45+ import org .jooby .internal .RouteSourceImpl ;
4346
4447import com .google .common .base .CaseFormat ;
4548import com .google .common .base .Strings ;
4851import com .google .common .primitives .Primitives ;
4952import com .google .inject .Key ;
5053import com .google .inject .TypeLiteral ;
54+ import com .google .inject .internal .util .SourceProvider ;
5155
5256import javaslang .CheckedFunction1 ;
5357
215219 */
216220public interface Route {
217221
222+ /**
223+ * Provides useful information about where the route was defined.
224+ *
225+ * @author edgar
226+ * @since 1.0.0.CR4
227+ */
228+ interface Source {
229+
230+ Source UNKNOWN = new Source () {
231+
232+ @ Override
233+ public int line () {
234+ return -1 ;
235+ }
236+
237+ @ Override
238+ public Optional <String > declaringClass () {
239+ return Optional .empty ();
240+ }
241+
242+ @ Override
243+ public String toString () {
244+ return "~unknown:" + line ();
245+ }
246+ };
247+
248+ /**
249+ * @return Line number where the route was defined or <code>-1</code> when not available.
250+ */
251+ int line ();
252+
253+ /**
254+ * @return Class where the route
255+ */
256+ Optional <String > declaringClass ();
257+ }
258+
218259 /**
219260 * The map operator converts a route output to something else
220261 *
@@ -966,6 +1007,9 @@ public Collection map(final Mapper<?> mapper) {
9661007 */
9671008 class Definition implements Props <Definition > {
9681009
1010+ private static final SourceProvider SRC = SourceProvider .DEFAULT_INSTANCE
1011+ .plusSkippedClasses (Definition .class , Jooby .class , Collection .class , Group .class );
1012+
9691013 /**
9701014 * Route's name.
9711015 */
@@ -1009,6 +1053,10 @@ class Definition implements Props<Definition> {
10091053
10101054 private Mapper <?> mapper ;
10111055
1056+ private int line ;
1057+
1058+ private String declaringClass ;
1059+
10121060 /**
10131061 * Creates a new route definition.
10141062 *
@@ -1062,6 +1110,9 @@ public Definition(final String method, final String pattern,
10621110 // normalized pattern
10631111 this .pattern = cpattern .pattern ();
10641112 this .filter = filter ;
1113+ StackTraceElement source = SRC .get (new Throwable ().getStackTrace ());
1114+ this .line = source .getLineNumber ();
1115+ this .declaringClass = source .getClassName ();
10651116 }
10661117
10671118 /**
@@ -1201,7 +1252,8 @@ public Optional<Route> matches(final String method,
12011252 // keep accept when */*
12021253 List <MediaType > produces = result .size () == 1 && result .get (0 ).name ().equals ("*/*" )
12031254 ? accept : this .produces ;
1204- return Optional .of (asRoute (method , matcher , produces ));
1255+ return Optional
1256+ .of (asRoute (method , matcher , produces , new RouteSourceImpl (declaringClass , line )));
12051257 }
12061258 }
12071259 return Optional .empty ();
@@ -1364,6 +1416,28 @@ public Definition map(final Mapper<?> mapper) {
13641416 return this ;
13651417 }
13661418
1419+ /**
1420+ * Set the line where this route is defined.
1421+ *
1422+ * @param line Line number.
1423+ * @return This instance.
1424+ */
1425+ public Definition line (final int line ) {
1426+ this .line = line ;
1427+ return this ;
1428+ }
1429+
1430+ /**
1431+ * Set the class where this route is defined.
1432+ *
1433+ * @param declaringClass A source class.
1434+ * @return This instance.
1435+ */
1436+ public Definition declaringClass (final String declaringClass ) {
1437+ this .declaringClass = declaringClass ;
1438+ return this ;
1439+ }
1440+
13671441 @ Override
13681442 public String toString () {
13691443 StringBuilder buffer = new StringBuilder ();
@@ -1381,12 +1455,13 @@ public String toString() {
13811455 * @param method A HTTP verb.
13821456 * @param matcher A route matcher.
13831457 * @param produces List of produces types.
1458+ * @param source Route source.
13841459 * @return A new route.
13851460 */
13861461 private Route asRoute (final String method , final RouteMatcher matcher ,
1387- final List <MediaType > produces ) {
1462+ final List <MediaType > produces , final Route . Source source ) {
13881463 return new RouteImpl (filter , this , method , matcher .path (), produces ,
1389- matcher .vars (), mapper );
1464+ matcher .vars (), mapper , source );
13901465 }
13911466
13921467 }
@@ -1473,6 +1548,21 @@ public String reverse(final Object... values) {
14731548 return route .reverse (values );
14741549 }
14751550
1551+ @ Override
1552+ public Source source () {
1553+ return route .source ();
1554+ }
1555+
1556+ @ Override
1557+ public String print () {
1558+ return route .print ();
1559+ }
1560+
1561+ @ Override
1562+ public String print (final int indent ) {
1563+ return route .print (indent );
1564+ }
1565+
14761566 @ Override
14771567 public String toString () {
14781568 return route .toString ();
@@ -2148,4 +2238,48 @@ default <T> T attr(final String name) {
21482238 static String normalize (final String path ) {
21492239 return RoutePattern .normalize (path );
21502240 }
2241+
2242+ /**
2243+ * @return Source information.
2244+ */
2245+ Route .Source source ();
2246+
2247+ /**
2248+ * Print route information like: method, path, source, etc... Useful for debugging.
2249+ *
2250+ * @param indent Indent level
2251+ * @return Output.
2252+ */
2253+ default String print (final int indent ) {
2254+ StringBuilder buff = new StringBuilder ();
2255+ String [] header = {"Method" , "Path" , "Source" , "Name" , "Pattern" , "Consumes" , "Produces" };
2256+ String [] values = {method (), path (), source ().toString (), name (), pattern (),
2257+ consumes ().toString (), produces ().toString () };
2258+
2259+ BiConsumer <Function <Integer , String >, Character > format = (v , s ) -> {
2260+ buff .append (Strings .padEnd ("" , indent , ' ' ))
2261+ .append ("|" ).append (s );
2262+ for (int i = 0 ; i < header .length ; i ++) {
2263+ buff
2264+ .append (Strings .padEnd (v .apply (i ), Math .max (header [i ].length (), values [i ].length ()), s ))
2265+ .append (s ).append ("|" ).append (s );
2266+ }
2267+ buff .setLength (buff .length () - 1 );
2268+ };
2269+ format .accept (i -> header [i ], ' ' );
2270+ buff .append ("\n " );
2271+ format .accept (i -> "-" , '-' );
2272+ buff .append ("\n " );
2273+ format .accept (i -> values [i ], ' ' );
2274+ return buff .toString ();
2275+ }
2276+
2277+ /**
2278+ * Print route information like: method, path, source, etc... Useful for debugging.
2279+ *
2280+ * @return Output.
2281+ */
2282+ default String print () {
2283+ return print (0 );
2284+ }
21512285}
0 commit comments