private void add(String workspaceName, PathToScan pathToScan) {
   Collection<PathToScan> pathsToScan = pathsByWorkspaceName.get(workspaceName);
   if (pathsToScan.isEmpty()) {
     pathsToScan.add(pathToScan);
   } else {
     Iterator<PathToScan> iter = pathsToScan.iterator();
     boolean add = true;
     final Path path = pathToScan.path();
     while (iter.hasNext()) {
       PathToScan existing = iter.next();
       Path existingPath = existing.path();
       if (path.isAtOrAbove(existingPath)) {
         // Remove all of the existing paths that are at or above this path (we'll add it back in
         // ...)
         iter.remove();
         // But add all of the callbacks ...
         pathToScan.addCallbacks(existing);
       } else if (path.isDescendantOf(existingPath)) {
         // The new path is a descendant of an existing path, so we can stop now and do nothing
         // ...
         add = false;
         // But add all of the callbacks ...
         existing.addCallbacks(pathToScan);
         break;
       }
     }
     if (add) pathsByWorkspaceName.put(workspaceName, pathToScan);
   }
 }
 /**
  * Scan each required path in each of the workspaces.
  *
  * @param operation the scanning operation that is to be called for each workspace & path
  *     combination; may not be null
  */
 public void onEachPathInWorkspace(ScanOperation operation) {
   for (Map.Entry<String, PathToScan> entry : pathsToScanByWorkspace.entries()) {
     String workspaceName = entry.getKey();
     PathToScan pathToScan = entry.getValue();
     try {
       for (IndexingCallback callback : pathToScan) {
         try {
           callback.beforeIndexing();
         } catch (RuntimeException e) {
           Logger.getLogger(getClass())
               .error(
                   e, JcrI18n.errorIndexing, pathToScan.path(), workspaceName, e.getMessage());
         }
       }
       operation.scan(workspaceName, pathToScan.path());
     } catch (RuntimeException e) {
       Logger.getLogger(getClass())
           .error(e, JcrI18n.errorIndexing, pathToScan.path(), workspaceName, e.getMessage());
     } finally {
       for (IndexingCallback callback : pathToScan) {
         try {
           callback.afterIndexing();
         } catch (RuntimeException e) {
           Logger.getLogger(getClass())
               .error(
                   e, JcrI18n.errorIndexing, pathToScan.path(), workspaceName, e.getMessage());
         }
       }
     }
   }
 }