docs: @Override
      public boolean removeListener(ArtifactListener listenerToRemove) {
          synchronized(listenersLock) {
              ArtifactListenerSelector listenerSelectorToRemove = new ArtifactListenerSelector(listenerToRemove);
              List<String> pathsToRemove = new ArrayList<String>(1);
              for ( Map.Entry<String, Collection<ArtifactListenerSelector>> listenersEntry : listeners.entrySet() ) {
                  for ( ArtifactListenerSelector listener : listenersEntry.getValue() ) {
                      if ( listener.equals(listenerSelectorToRemove)) {
                          pathsToRemove.add( listenersEntry.getKey() );
                          break;
                      }
                  }
              }
  
              Iterator<String> usePathsToRemove = pathsToRemove.iterator();
              while ( usePathsToRemove.hasNext() ) {
                  String pathToRemove = usePathsToRemove.next();
                  Collection<ArtifactListenerSelector> listenersForPath = listeners.get(pathToRemove);
                  if ( listenersForPath.size() == 1 ) {
                      listeners.remove(pathToRemove); // The last listner for the path.
                  } else {
                      listenersForPath.remove(listenerSelectorToRemove);
                      usePathsToRemove.remove();
                  }
              }
  
              if ( !pathsToRemove.isEmpty() ) {
                  // TODO: Clearing and rebuilding the covering paths collection
                  //       Not optimal, but hard to do better, and not necessary
                  //       since removals should be rare and the registrations
                  //       are expected to be small.
                  //
                  // Each path which was removed which is a covering path
                  // may uncover paths.  Those need to be found and added
                  // to the covering paths collection.
                  //
                  // But, not all paths which were covered should be added,
                  // as some the newly uncovered paths may cover other of
                  // the newly uncovered paths.
                  //
                  // Also, an uncovered path which is discovered may itself
                  // be a path to removed which hasn't yet been processed.
  
                  coveringPaths.clear();
  
                  for ( String listenerPath : listeners.keySet() ) {
                      @SuppressWarnings("unused")
                      boolean addedUncoveredPath = addCoveringPath(listenerPath);
                  }
  
                  // Don't know if the covering paths collection was perturbed.
                  // Assume that it was.
                  updateMonitor();
              }
  
              return ( !pathsToRemove.isEmpty() );
          }
      }