static private List<EventStripe> mergeClustersToStripes(Period timeUnitLength, List<EventCluster> preMergedEvents) {
        //effectively map from type to (map from description to events)
        Map<EventType, SetMultimap< String, EventCluster>> typeMap = new HashMap<>();
        for (EventCluster aggregateEvent : preMergedEvents) {
            typeMap.computeIfAbsent(aggregateEvent.getEventType(), eventType -> HashMultimap.create())
                    .put(aggregateEvent.getDescription(), aggregateEvent);
        }
        //result list to return
        ArrayList<EventCluster> aggEvents = new ArrayList<>();
        //For each (type, description) key, merge agg events
        for (SetMultimap<String, EventCluster> descrMap : typeMap.values()) {
            //for each description ...
            for (String descr : descrMap.keySet()) {
                //run through the sorted events, merging together adjacent events
                Iterator<EventCluster> iterator = descrMap.get(descr).stream()
                        .sorted(Comparator.comparing(event -> event.getSpan().getStartMillis()))
                        .iterator();
                EventCluster current = iterator.next();
                while (iterator.hasNext()) {
                    EventCluster next = iterator.next();
                    Interval gap = current.getSpan().gap(next.getSpan());
                    //if they overlap or gap is less one quarter timeUnitLength
                    //TODO: 1/4 factor is arbitrary. review! -jm
                    if (gap == null || gap.toDuration().getMillis() <= timeUnitLength.toDurationFrom(gap.getStart()).getMillis() / 4) {
                        //merge them
                        current = EventCluster.merge(current, next);
                    } else {
                        //done merging into current, set next as new current
                        aggEvents.add(current);
                        current = next;
                    }
                }
                aggEvents.add(current);
            }
        }
        //merge clusters to stripes
        Map<ImmutablePair<EventType, String>, EventStripe> stripeDescMap = new HashMap<>();
        for (EventCluster eventCluster : aggEvents) {
            stripeDescMap.merge(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()),
                    new EventStripe(eventCluster, null), EventStripe::merge);
        }
        return stripeDescMap.values().stream().sorted(Comparator.comparing(EventStripe::getStartMillis)).collect(Collectors.toList());
    }
static private List<EventStripe> mergeClustersToStripes(Period timeUnitLength, List<EventCluster> preMergedEvents) {
       
        Map<EventType, SetMultimap< String, EventCluster>> typeMap = new HashMap<>();
        for (EventCluster aggregateEvent : preMergedEvents) {
            typeMap.computeIfAbsent(aggregateEvent.getEventType(), eventType -> HashMultimap.create())
                    .put(aggregateEvent.getDescription(), aggregateEvent);
        }
       
        ArrayList<EventCluster> aggEvents = new ArrayList<>();
       
        for (SetMultimap<String, EventCluster> descrMap : typeMap.values()) {
           
            for (String descr : descrMap.keySet()) {
               
                Iterator<EventCluster> iterator = descrMap.get(descr).stream()
                        .sorted(Comparator.comparing(event -> event.getSpan().getStartMillis()))
                        .iterator();
                EventCluster current = iterator.next();
                while (iterator.hasNext()) {
                    EventCluster next = iterator.next();
                    Interval gap = current.getSpan().gap(next.getSpan());
                   
                   
                    if (gap == null || gap.toDuration().getMillis() <= timeUnitLength.toDurationFrom(gap.getStart()).getMillis() / 4) {
                       
                        current = EventCluster.merge(current, next);
                    } else {
                       
                        aggEvents.add(current);
                        current = next;
                    }
                }
                aggEvents.add(current);
            }
        }
       
        Map<ImmutablePair<EventType, String>, EventStripe> stripeDescMap = new HashMap<>();
        for (EventCluster eventCluster : aggEvents) {
            stripeDescMap.merge(ImmutablePair.of(eventCluster.getEventType(), eventCluster.getDescription()),
                    new EventStripe(eventCluster, null), EventStripe::merge);
        }
        return stripeDescMap.values().stream().sorted(Comparator.comparing(EventStripe::getStartMillis)).collect(Collectors.toList());
    }
static private list<eventstripe> mergeclusterstostripes(period timeunitlength, list<eventcluster> premergedevents) { map<eventtype, setmultimap< string, eventcluster>> typemap = new hashmap<>(); for (eventcluster aggregateevent : premergedevents) { typemap.computeifabsent(aggregateevent.geteventtype(), eventtype -> hashmultimap.create()) .put(aggregateevent.getdescription(), aggregateevent); } arraylist<eventcluster> aggevents = new arraylist<>(); for (setmultimap<string, eventcluster> descrmap : typemap.values()) { for (string descr : descrmap.keyset()) { iterator<eventcluster> iterator = descrmap.get(descr).stream() .sorted(comparator.comparing(event -> event.getspan().getstartmillis())) .iterator(); eventcluster current = iterator.next(); while (iterator.hasnext()) { eventcluster next = iterator.next(); interval gap = current.getspan().gap(next.getspan()); if (gap == null || gap.toduration().getmillis() <= timeunitlength.todurationfrom(gap.getstart()).getmillis() / 4) { current = eventcluster.merge(current, next); } else { aggevents.add(current); current = next; } } aggevents.add(current); } } map<immutablepair<eventtype, string>, eventstripe> stripedescmap = new hashmap<>(); for (eventcluster eventcluster : aggevents) { stripedescmap.merge(immutablepair.of(eventcluster.geteventtype(), eventcluster.getdescription()), new eventstripe(eventcluster, null), eventstripe::merge); } return stripedescmap.values().stream().sorted(comparator.comparing(eventstripe::getstartmillis)).collect(collectors.tolist()); }