Class PathBasedMenuBuilder

java.lang.Object
com.foreach.across.modules.web.menu.PathBasedMenuBuilder

public class PathBasedMenuBuilder extends Object
A PathBasedMenuBuilder can be used to define menu items in a non-hierarchical way. Items are registered using a unique path. Every item with a path that is also the prefix of another item's path, will become the parent item of those other items.

This will result in a sub-tree being created. The / (forward slash) is the path separator for prefix candidates.

Example

Suppose you register the following menu items in order:

  1. /my-group/item-1
  2. /my-group
  3. /my-item
  4. /my-group/item-2
  5. /my-other-group/single-item

or in code:


 builder.item( "/my-group/item-1" ).and()
        .item( "/my-group" ).and()
        .item( "/my-item" ).and()
        .item( "/my-group/item-2" ).and()
        .item( "/my-other-group/single-item" ).and()
        .item( "/my-group:item-3" ).and()
        .build()
 
When building the Menu this will result in the following hierarchy:

 ROOT
   + /my-group
   |   + /my-group/item-1
   |   + /my-group/item-2
   + /my-group:item-3
   + /my-item
   + /my-other-group/single-item
 

Note that a parent item does not automatically get created based on path separator (the example of /my-other-group/single-item. Only if there is another item with an exact path of that before a separator will an item be created. This is also why /my-group:item-3 is still a separate item as : (colon) does not count as a path separator.

By default the top-most item of the menu has no specific path. Setting a path on the root item can be done using root(String), but this will have no impact on the hierarchy being created. The root path of a Menu is only relevant in specialized cases where you want to merge the result of a builder into an already existing Menu.

Order and sorting

A Menu item can have an order specified which can be used to sort the Menu. Sorting a menu is usually done externally, the PathBasedMenuBuilder creates a Menu with the items ordered according to their path value. You can see this in the above example.

You can manually set a Comparator that should be used for sorting a menu. You can so do per item, in which case the comparator will be used only for the children of that particular item. Depending on how you register it, it will apply for all sub-trees of a menu item.

If you want to use a different Comparator for the entire menu, you must register it on the root item:


 builder.root( "/root" ).comparator( myComparator, true );
 

Group items

A single item can also be flagged as a group (this sets the value of Menu.isGroup(). This property however has nothing to do with the actual hierarchy being created. It is not because an item is flagged as a group that it will be turned into a parent for other items. It will automatically serve as a parent if the other items have a path that uses the current item as prefix. The group property is an indication for how the item should behave in the generated Menu. The value of Menu.isGroup() is never set automatically, not even when an item becomes the parent of others. You should create groups using group(String).

From item path to Menu hierarchy

The PathBaseMenuBuilder has itself no concept of a hierarchy of menu items. It purely works on a map of items identified by their path. It is only when the actual Menu is being built that this flat list of items gets turned into a Menu hierarchy, based on the presence of path separators (/ - forward slash) and items matching sub-segments of others.

Since:
1.0.0
Author:
Arne Vandamme, Marc Vanbrabant
See Also:
  • Constructor Details

  • Method Details

    • withProcessor

      public PathBasedMenuBuilder withProcessor(@NonNull @NonNull MenuItemBuilderProcessor processor, @NonNull @NonNull Consumer<PathBasedMenuBuilder> consumer)
      Perform a set of actions with a different item processor. This creates a new builder based on this one but having a different processor applied after menu item building. The new builder will have the same configuration, and any item builders modified will immediately apply to both the original and the new builder. Only when building will a different processor for these items be used.

      Use this method if you want to apply custom processing to a subset of menu items in this menu. This does not change the current builder, only creates a new one that is passed to the consumer.

      If you want to effectively remove an already attached processor, you can use this method with the MenuItemBuilderProcessor.NoOpProcessor as parameter.

      Parameters:
      processor - that should be applied to the scoped version
      consumer - for performing actions on the scoped builder
      Returns:
      original menu builder
      See Also:
    • root

      public PathBasedMenuBuilder.PathBasedMenuItemBuilder root(@NonNull @NonNull String rootPath)
      Get the root item builder. By default the root item of the menu has no path, this method also configures a path on the root item.
      Parameters:
      rootPath - path of the top most menu
      Returns:
      root item builder
    • item

      public PathBasedMenuBuilder.PathBasedMenuItemBuilder item(@NonNull @NonNull String path)
      Retrieve the item builder for a specific path. If there is none yet, one will be created.
      Parameters:
      path - identifying the item
      Returns:
      item builder
    • optionalItem

      public PathBasedMenuBuilder.PathBasedMenuItemBuilder optionalItem(@NonNull @NonNull String path)
      Return an item builder for updating an item if it exists. This will always return a valid item builder, but nothing will happen if that item did not exist before.
      Parameters:
      path - identifying the item
      Returns:
      item builder
    • item

      Retrieve the item builder for a specific path. If there is none yet, one will be created.

      This method is a shorter version for item(path).title(title).

      Parameters:
      path - identifying the item
      title - that should be set on the item
      Returns:
      item builder
    • item

      Retrieve the item builder for a specific path. If there is none yet, one will be created.

      This method is a shorter version for item(path).title(title).url(url).

      Parameters:
      path - identifying the item
      title - that should be set on the item
      url - that should be set on the item
      Returns:
      item builder
    • group

      Retrieve the item builder for a specific path, where the item should represent a group of items. If there is no item builder yet, one will be created. If the item builder exists, but is not yet flagged as a group, it will be turned into a group.

      Note that flagging an item as a group simply sets the appropriate property. It has no effect on the actual Menu hierarchy being built and the fact that this item might serve as a parent for others. The latter is purely determined by the path splitting when building the menu.

      This method is a shorter version for item(path).group(true).

      Parameters:
      path - identifying the item
      Returns:
      item builder
    • group

      Retrieve the item builder for a specific path, where the item should represent a group of items. If there is no item builder yet, one will be created. If the item builder exists, but is not yet flagged as a group, it will be turned into a group.

      Note that flagging an item as a group simply sets the appropriate property. It has no effect on the actual Menu hierarchy being built and the fact that this item might serve as a parent for others. The latter is purely determined by the path splitting when building the menu.

      This method is a shorter version for item(path, title).group(true).

      Parameters:
      path - identifying the item
      title - that should be set on the item
      Returns:
      item builder
    • andThen

      public PathBasedMenuBuilder andThen(@NonNull @NonNull Consumer<PathBasedMenuBuilder> consumer)
      Add an additional Consumer for this PathBasedMenuBuilder that should be applied right before the actual Menu is being built.

      Use this method sparingly, only when you want to customize the menu builder and wish to be sure that initial configuration has been applied. For example you want to move a group of items but you are not sure up front how many child items it will have as modules could register them at a later stage. Shifting the path changing calls to the separate andThen(Consumer) consumer can help you in said case.

      Note that once the consumer has been applied, it will not be re-applied on subsequent builds. It is possible to register additional consumers from within a consumer, but simplicity's sake you probably want to avoid this.

      Parameters:
      consumer - to add
      Returns:
      current builder
    • removeItems

      public PathBasedMenuBuilder removeItems(@NonNull @NonNull String path, boolean removeAllItemsWithPathAsPrefix)
      Remove the item with the specified path from this menu builder. Optionally also removes all other items having the specified path as prefix.
      Parameters:
      path - to remove
      removeAllItemsWithPathAsPrefix - true if other items with that path as prefix should be removed as well
      Returns:
      current builder
    • changeItemPath

      public PathBasedMenuBuilder changeItemPath(@NonNull @NonNull String currentPathPrefix, @NonNull @NonNull String newPathPrefix)
      Update the path of all items starting with (or equal to) the given path prefix. The prefix of the current path will be updated to the new path prefix. If you only want to change the single item with exactly that path, use changeItemPath("currentPrefix", "newPrefix", false).

      Note that, unlike item("my item").changePathTo(X), this method will not create any items if they do not exist.

      Parameters:
      currentPathPrefix - prefix item paths should be starting with
      newPathPrefix - new prefix to use
      Returns:
      current menu builder
      Since:
      3.0.0
      See Also:
    • changeItemPath

      public PathBasedMenuBuilder changeItemPath(@NonNull @NonNull String currentPathPrefix, @NonNull @NonNull String newPathPrefix, boolean replaceAllItemsWithPrefix)
      Update the path of all items starting with (or equal to) the given path prefix. The prefix of the current path will be updated to the new path prefix. If you only want to change the single item starting with exactly that path, use changeItemPaths("currentPrefix", "newPrefix", false).

      Note that, unlike item("my item").changePathTo(X), this method will not create any items if they do not exist.

      Parameters:
      currentPathPrefix - prefix item paths should be starting with
      newPathPrefix - new prefix to use
      replaceAllItemsWithPrefix - true if all items starting with the currentPathPrefix should be updated, false if only an exact match should update
      Returns:
      current menu builder
      Since:
      3.0.0
    • build

      public Menu build()
      Returns:
      A newly constructed Menu instance.
    • build

      public void build(Menu menu)
      Will build into the existing menu and merge the root only if the root has been configured on the builder.
      Parameters:
      menu - Menu instance that should contain the result.
      See Also:
    • merge

      public void merge(Menu menu)
      Will merge into the existing menu and merge the root only if it has been configured on the builder.
      Parameters:
      menu - Menu instance that should contain the result.
      See Also:
    • build

      public void build(Menu menu, boolean ignoreRoot)
      Builds the result of the builder in the existing instance. This clears the instance and considers it as the root for the new menu. Only the root node will be merged and the reference to the parent will be kept, all existing child items will be deleted. If you want to merge the 2 menu items, see Merge instead.
      Parameters:
      menu - Menu instance that should contain the result.
      ignoreRoot - True if the root of the original menu should be kept as is.
      See Also:
    • merge

      public void merge(Menu menu, boolean ignoreRoot)
      Merges the result of the builder in the existing instance. Any items already present in the existing menu will be kept or modified if they have the same path. If you do not care about the existing instance, use build instead.
      Parameters:
      menu - Menu instance that should contain the result.
      ignoreRoot - True if the root of the original menu should be kept as is.
      See Also:
    • getParent

      protected PathBasedMenuBuilder getParent()