docs: @Override
      public synchronized void onChange(Collection<File> createdFiles, Collection<File> modifiedFiles, Collection<File> deletedFiles, String filter) {
          //scan the collections of absolute paths, and resolve them into sets of vfs locations
          //this involves not just abs->relative translation but also
          // - removing obscured notifications
          // - converting deleted into modified as appropriate
          Set<String> fRelativeAddedSet = new HashSet<String>();
          Set<String> fRelativeModifiedSet = new HashSet<String>();
          Set<String> fRelativeRemovedSet = new HashSet<String>();
          constructRelativeNotificationPaths(fRelativeAddedSet, fRelativeModifiedSet, fRelativeRemovedSet, createdFiles, modifiedFiles, deletedFiles);
  
          //if any of the sets are not empty, we may need to push a notification to parent.
          if (parent != null && (!fRelativeAddedSet.isEmpty() || !fRelativeModifiedSet.isEmpty() || !fRelativeRemovedSet.isEmpty())) {
              //notify as a modified of pathInParent.
              parent.notifyListenersByPath(Collections.<String> emptySet(), Collections.<String> singleton(pathInParent), Collections.<String> emptySet(), filter);
          }
  
          //now we notify all our listeners.
          notifyListenersByPath(fRelativeAddedSet, fRelativeModifiedSet, fRelativeRemovedSet, filter);
      }