public FilterRoutingInfo fillInInterestRoutingInfo(CacheEvent event, Profile[] profiles,
      FilterRoutingInfo filterRoutingInfo, Set cacheOpRecipients) {
    Set clientsInv = Collections.emptySet();
    Set clients = Collections.emptySet();

    if (logger.isTraceEnabled(LogMarker.BRIDGE_SERVER_VERBOSE)) {
      logger.trace(LogMarker.BRIDGE_SERVER_VERBOSE, "finding interested clients for {}", event);
    }

    FilterRoutingInfo frInfo = filterRoutingInfo;

    for (final Profile profile : profiles) {
      CacheProfile cf = (CacheProfile) profile;

      if (!cf.hasCacheServer) {
        continue;
      }

      FilterProfile pf = cf.filterProfile;

      if (pf == null) {
        continue;
      }

      if (logger.isTraceEnabled(LogMarker.BRIDGE_SERVER_VERBOSE)) {
        logger.trace(LogMarker.BRIDGE_SERVER_VERBOSE, "Processing {}", pf);
      }

      if (!pf.hasInterest()) {
        // This block sends an empty routing table to a member that's going to
        // get a CacheDistributionMessage so that if, in flight, there is an
        // interest registration change the version held in the routing table
        // can be used to detect the change and the receiver can recompute
        // the routing.
        if (!pf.isLocalProfile() && cacheOpRecipients.contains(cf.getDistributedMember())) {
          if (frInfo == null) {
            frInfo = new FilterRoutingInfo();
          }
          frInfo.addInterestedClients(cf.getDistributedMember(), Collections.emptySet(),
              Collections.emptySet(), false);
        }
        continue;
      }

      if (event.getOperation().isEntry()) {
        EntryEvent entryEvent = (EntryEvent) event;
        clientsInv = pf.getInterestedClients(entryEvent, pf.allKeyClientsInv,
            pf.keysOfInterestInv, pf.patternsOfInterestInv, pf.filtersOfInterestInv);
        clients = pf.getInterestedClients(entryEvent, pf.allKeyClients, pf.keysOfInterest,
            pf.patternsOfInterest, pf.filtersOfInterest);
      } else {
        if (event.getOperation().isRegionDestroy() || event.getOperation().isClear()) {
          clientsInv = pf.getAllClientsWithInterestInv();
          clients = pf.getAllClientsWithInterest();
        } else {
          return frInfo;
        }
      }

      if (pf.isLocalProfile) {
        if (logger.isDebugEnabled()) {
          logger.debug("Setting local interested clients={} and clientsInv={}", clients,
              clientsInv);
        }
        if (frInfo == null) {
          frInfo = new FilterRoutingInfo();
        }
        frInfo.setLocalInterestedClients(clients, clientsInv);
      } else {
        if (cacheOpRecipients.contains(cf.getDistributedMember()) || // always send a routing with
        // CacheOperationMessages
            (clients != null && !clients.isEmpty())
            || (clientsInv != null && !clientsInv.isEmpty())) {
          if (logger.isDebugEnabled()) {
            logger.debug("Adding interested clients={} and clientsIn={} to {}", clients, clientsInv,
                filterRoutingInfo);
          }
          if (frInfo == null) {
            frInfo = new FilterRoutingInfo();
          }
          frInfo.addInterestedClients(cf.getDistributedMember(), clients, clientsInv,
              clientMap.hasLongID);
        }
      }
    }
    return frInfo;
  }
Fills in the routing information for clients that have registered interest in the given event.
The routing information is stored in the given FilterRoutingInfo object for use in message
delivery.

@param event the event being applied to the cache
@param profiles the profiles of members having the affected region
@param filterRoutingInfo the routing object that is modified by this method (may be null)
@param cacheOpRecipients members that will receive a CacheDistributionMessage for the event
@return the resulting FilterRoutingInfo
geode-core/src/main/java/org/apache/geode/internal/cache/FilterProfile.java