2323import java .util .LinkedHashMap ;
2424import java .util .List ;
2525import java .util .Map ;
26+ import java .util .Map .Entry ;
2627import java .util .Objects ;
2728import java .util .Optional ;
2829import java .util .Set ;
3132import java .util .regex .Pattern ;
3233
3334import com .vaadin .flow .component .Component ;
35+ import com .vaadin .flow .router .Route ;
36+ import com .vaadin .flow .router .RouteAlias ;
3437import com .vaadin .flow .router .RouteParameters ;
3538import com .vaadin .flow .server .AmbiguousRouteConfigurationException ;
3639
@@ -101,11 +104,18 @@ final class RouteSegment implements Serializable {
101104 */
102105 private Map <String , RouteSegment > allSegments ;
103106
104- private RouteSegment () {
105- }
107+ private final boolean isRoot ;
108+
109+ /**
110+ * Main route segment should be returned first in the {@link #getRoutes()}
111+ * method to support {@link Route}/{@link RouteAlias} annotation semantic :
112+ * {@link Route} should take precedence over {@link RouteAlias}.
113+ */
114+ private boolean isMainRouteSegment ;
106115
107- private RouteSegment (String segmentTemplate ) {
116+ private RouteSegment (String segmentTemplate , boolean isRoot ) {
108117 this .template = segmentTemplate ;
118+ this .isRoot = isRoot ;
109119
110120 if (RouteFormat .isParameter (segmentTemplate )) {
111121 info = new RouteFormat .ParameterInfo (segmentTemplate );
@@ -124,6 +134,8 @@ public RouteSegment(RouteSegment original) {
124134 this .info = original .info ;
125135 this .pattern = original .pattern ;
126136 this .target = original .target ;
137+ this .isRoot = original .isRoot ;
138+ this .isMainRouteSegment = original .isMainRouteSegment ;
127139
128140 original .getStaticSegments ().entrySet ()
129141 .forEach (e -> this .addSegment (new RouteSegment (e .getValue ()),
@@ -146,7 +158,7 @@ public RouteSegment(RouteSegment original) {
146158 * Create a new root segment instance.
147159 */
148160 static RouteSegment createRoot () {
149- return new RouteSegment ("" );
161+ return new RouteSegment ("" , true );
150162 }
151163
152164 String getName () {
@@ -203,24 +215,38 @@ boolean isEligible(String value) {
203215 * @return a {@link Map} containing all templates and their specific
204216 * targets.
205217 */
206- Map <String , RouteTarget > getRoutes () {
207-
208- Map <String , RouteTarget > result = new LinkedHashMap <>();
218+ LinkedHashMap <String , RouteTarget > getRoutes () {
219+
220+ Map <String , RouteSegment > leafSegments = getLeafStaticSegments ();
221+
222+ String mainRoutePath = null ;
223+ RouteTarget mainRouteTarget = null ;
224+
225+ /*
226+ * Find the first main route segment in the static segments which should
227+ * go first to be able to support Route/RouteAlias semantic. Main route
228+ * doesn't have to exist though: it may be removed at any point. In this
229+ * case the order doesn't matter since the RouteAlias ordering is not
230+ * defined.
231+ */
232+ for (Entry <String , RouteSegment > entry : leafSegments .entrySet ()) {
233+ if (entry .getValue ().isMainRouteSegment ) {
234+ mainRoutePath = entry .getKey ();
235+ mainRouteTarget = entry .getValue ().target ;
236+ break ;
237+ }
238+ }
209239
210- if (target != null ) {
211- result .put ("" , target );
240+ // If there is the main route : add it as first
241+ LinkedHashMap <String , RouteTarget > result = new LinkedHashMap <>();
242+ if (mainRoutePath != null ) {
243+ result .put (mainRoutePath , mainRouteTarget );
212244 }
213245
214- collectRoutes (result , getStaticSegments ());
215- collectRoutes (result , getParameterSegments ());
216- collectRoutes (result , getOptionalSegments ());
217- collectRoutes (result , getVarargsSegments ());
246+ getLeafSegments ()
247+ .forEach ((path , segment ) -> result .put (path , segment .target ));
218248
219- if (getTemplate ().isEmpty ()) {
220- return Collections .unmodifiableMap (result );
221- } else {
222- return result ;
223- }
249+ return result ;
224250 }
225251
226252 void removeSubRoute (String template ) {
@@ -292,13 +318,53 @@ String formatTemplate(String template,
292318 }
293319 }
294320
295- private void collectRoutes (Map <String , RouteTarget > result ,
321+ private LinkedHashMap <String , RouteSegment > getLeafSegments () {
322+ LinkedHashMap <String , RouteSegment > result = new LinkedHashMap <>();
323+
324+ if (target != null ) {
325+ result .put ("" , this );
326+ }
327+
328+ collectLeafSegments (result , getStaticSegments ());
329+ collectLeafSegments (result , getParameterSegments ());
330+ collectLeafSegments (result , getOptionalSegments ());
331+ collectLeafSegments (result , getVarargsSegments ());
332+
333+ return result ;
334+ }
335+
336+ private Map <String , RouteSegment > getLeafStaticSegments () {
337+ Map <String , RouteSegment > result = new HashMap <>();
338+
339+ if (target != null ) {
340+ result .put ("" , this );
341+ }
342+
343+ for (Map .Entry <String , RouteSegment > segmentEntry : getStaticSegments ()
344+ .entrySet ()) {
345+ RouteSegment segment = segmentEntry .getValue ();
346+
347+ for (Map .Entry <String , RouteSegment > targetEntry : segment
348+ .getLeafStaticSegments ().entrySet ()) {
349+
350+ final String key = targetEntry .getKey ();
351+ result .put (
352+ segmentEntry .getKey ()
353+ + (key .isEmpty () ? "" : ("/" + key )),
354+ targetEntry .getValue ());
355+ }
356+ }
357+ return result ;
358+ }
359+
360+ private void collectLeafSegments (Map <String , RouteSegment > result ,
296361 Map <String , RouteSegment > children ) {
297362 for (Map .Entry <String , RouteSegment > segmentEntry : children
298363 .entrySet ()) {
364+ RouteSegment segment = segmentEntry .getValue ();
299365
300- for (Map .Entry <String , RouteTarget > targetEntry : segmentEntry
301- .getValue (). getRoutes ().entrySet ()) {
366+ for (Map .Entry <String , RouteSegment > targetEntry : segment
367+ .getLeafSegments ().entrySet ()) {
302368
303369 final String key = targetEntry .getKey ();
304370 result .put (
@@ -309,6 +375,16 @@ private void collectRoutes(Map<String, RouteTarget> result,
309375 }
310376 }
311377
378+ private RouteSegment getFirstLeafSegment () {
379+ if (target != null ) {
380+ return this ;
381+ }
382+ Map <String , RouteSegment > segments = getAllSegments ();
383+ assert !segments .isEmpty ();
384+ RouteSegment first = segments .values ().iterator ().next ();
385+ return first .getFirstLeafSegment ();
386+ }
387+
312388 private void removeSubRoute (List <String > segmentPatterns ) {
313389 RouteSegment routeSegment ;
314390 String segmentPattern = null ;
@@ -341,6 +417,7 @@ private void removeSubRoute(List<String> segmentPatterns) {
341417 }
342418
343419 private void addSubRoute (List <String > segmentPatterns , RouteTarget target ) {
420+ boolean isMainRoute = isEmpty () && isRoot ;
344421
345422 RouteSegment routeSegment ;
346423 String segmentPattern = null ;
@@ -379,6 +456,11 @@ private void addSubRoute(List<String> segmentPatterns, RouteTarget target) {
379456 }
380457
381458 routeSegment .setRouteTarget (segmentPatterns , target );
459+
460+ if (isMainRoute ) {
461+ RouteSegment firstSegment = getFirstLeafSegment ();
462+ firstSegment .isMainRouteSegment = true ;
463+ }
382464 }
383465
384466 private void setRouteTarget (List <String > segmentPatterns ,
@@ -730,7 +812,7 @@ boolean isEmpty() {
730812
731813 private RouteSegment addSegment (String segmentTemplate ,
732814 Map <String , RouteSegment > children ) {
733- RouteSegment routeSegment = new RouteSegment (segmentTemplate );
815+ RouteSegment routeSegment = new RouteSegment (segmentTemplate , false );
734816 addSegment (routeSegment , children );
735817 return routeSegment ;
736818 }
0 commit comments