/**
  * Adds the given modules to the menu structure. If a module is not visible (i.e., {@link
  * ModuleInfo#isVisible()} returns false), it is ignored.
  *
  * <p>{@inheritDoc}
  */
 @Override
 public boolean addAll(final Collection<? extends ModuleInfo> c) {
   final HashSet<ShadowMenu> nodes = new HashSet<>();
   for (final ModuleInfo info : c) {
     if (!info.isVisible()) continue;
     final ShadowMenu node = addInternal(info);
     if (node != null) nodes.add(node);
   }
   if (nodes.isEmpty()) return false;
   if (es != null) es.publish(new MenusAddedEvent(nodes));
   return true;
 }
 private String details(final ModuleInfo info) {
   if (info == null) return "<null>";
   String className, classLocation;
   try {
     final Class<?> c = info.loadDelegateClass();
     className = c.getName();
     classLocation = ClassUtils.getLocation(c).toString();
   } catch (final ClassNotFoundException exc) {
     className = info.getDelegateClassName();
     classLocation = "<invalid>";
   }
   return info.getMenuPath() + " : " + className + " [" + classLocation + "]";
 }
  private ShadowMenu addChild(final ModuleInfo info, final int depth) {
    final MenuPath menuPath = info.getMenuPath();
    final MenuEntry entry = menuPath.get(depth);
    final boolean leaf = isLeaf(depth, menuPath);

    // retrieve existing child
    final ShadowMenu existingChild = children.get(entry.getName());

    final ShadowMenu child;
    if (existingChild == null) {
      // create new child and add to table
      final String menuName = entry.getName();
      final ShadowMenu newChild = new ShadowMenu(getContext(), info, depth, this);
      children.put(menuName, newChild);
      child = newChild;
    } else {
      // fill in any missing menu properties of existing child
      final MenuEntry childMenuEntry = existingChild.getMenuEntry();
      childMenuEntry.assignProperties(entry);
      child = existingChild;
    }

    // recursively add remaining child menus
    if (!leaf) child.addChild(info, depth + 1);
    else if (existingChild != null) {
      if (log != null) {
        final ModuleInfo childInfo = existingChild.getModuleInfo();
        if (childInfo != null && info.getPriority() == childInfo.getPriority()) {
          log.warn(
              "ShadowMenu: menu item already exists:\n"
                  + //
                  "\texisting: "
                  + details(childInfo)
                  + "\n"
                  + //
                  "\t ignored: "
                  + details(info));
        } else {
          log.debug(
              "ShadowMenu: higher-priority menu item already exists:\n"
                  + "\texisting: "
                  + details(childInfo)
                  + "\n"
                  + //
                  "\t ignored: "
                  + details(info));
        }
      }
    }
    return child;
  }
  /**
   * Adds the given module to the menu structure. If the module is not visible (i.e., {@link
   * ModuleInfo#isVisible()} returns false), it is ignored.
   *
   * <p>{@inheritDoc}
   */
  @Override
  public boolean add(final ModuleInfo o) {
    if (!o.isVisible()) return false;

    final ShadowMenu node = addInternal(o);
    if (node == null) return false;
    if (es != null) es.publish(new MenusAddedEvent(node));
    return true;
  }
 private ShadowMenu(
     final Context context,
     final ModuleInfo moduleInfo,
     final int menuDepth,
     final ShadowMenu parent) {
   setContext(context);
   if (moduleInfo == null) {
     this.moduleInfo = null;
     menuEntry = null;
   } else {
     final MenuPath menuPath = moduleInfo.getMenuPath();
     // preserve moduleInfo reference only for leaf items
     final boolean leaf = menuDepth == menuPath.size() - 1;
     this.moduleInfo = leaf ? moduleInfo : null;
     menuEntry = menuPath.get(menuDepth);
   }
   this.menuDepth = menuDepth;
   this.parent = parent;
   children = new HashMap<>();
 }
 /**
  * Gets the URL of the icon associated with this node's {@link MenuEntry}.
  *
  * @see org.scijava.plugin.PluginInfo#getIconURL()
  */
 public URL getIconURL() {
   if (menuEntry == null) return null;
   String iconPath = menuEntry.getIconPath();
   if (iconPath == null || iconPath.isEmpty()) {
     if (isLeaf()) iconPath = DEFAULT_ICON_PATH;
     else return null;
   }
   final String className = moduleInfo.getDelegateClassName();
   try {
     final Class<?> c = ClassUtils.loadClass(className, false);
     final URL iconURL = c.getResource(iconPath);
     if (iconURL == null) {
       if (log != null) log.error("Could not load icon: " + iconPath);
     }
     return iconURL;
   } catch (final IllegalArgumentException exc) {
     final String message = "Could not load icon for class: " + className;
     if (log.isDebug()) log.debug(message, exc);
     else log.error(message);
     return null;
   }
 }
 private ShadowMenu addInternal(final ModuleInfo o) {
   if (o.getMenuPath().isEmpty()) return null; // no menu
   return addChild(o, 0);
 }
 /** Returns true if this node is a radio button. */
 public boolean isRadioButton() {
   if (!isToggle()) return false;
   final String selectionGroup = moduleInfo.getSelectionGroup();
   return selectionGroup != null && !selectionGroup.isEmpty();
 }
 /** Returns true if this node is a checkbox. */
 public boolean isCheckBox() {
   if (!isToggle()) return false;
   final String selectionGroup = moduleInfo.getSelectionGroup();
   return selectionGroup == null || selectionGroup.isEmpty();
 }
 /** Returns true if this node is selectable (checkbox or radio button). */
 public boolean isToggle() {
   if (moduleInfo == null) return false;
   return moduleInfo.isSelectable();
 }