/*
* Autopsy Forensic Browser
*
* Copyright 2015-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.datamodel;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.SortedSet;
import javax.annotation.concurrent.Immutable;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
/**
* A 'collection' of {@link EventCluster}s, all having the same type,
* description, and zoom levels, but not necessarily close together in time.
*/
@Immutable
public final class EventStripe implements MultiEvent<EventCluster> {
public static EventStripe merge(EventStripe u, EventStripe v) {
Preconditions.checkNotNull(u);
Preconditions.checkNotNull(v);
Preconditions.checkArgument(Objects.equals(u.description, v.description));
Preconditions.checkArgument(Objects.equals(u.lod, v.lod));
Preconditions.checkArgument(Objects.equals(u.type, v.type));
Preconditions.checkArgument(Objects.equals(u.parent, v.parent));
return new EventStripe(u, v);
}
private final EventCluster parent;
private final ImmutableSortedSet<EventCluster> clusters;
/**
* the type of all the events
*/
private final EventType type;
/**
* the common description of all the events
*/
private final String description;
/**
* the description level of detail that the events were clustered at.
*/
private final DescriptionLoD lod;
/**
* the set of ids of the events
*/
private final ImmutableSet<Long> eventIDs;
/**
* the ids of the subset of events that have at least one tag applied to
* them
*/
private final ImmutableSet<Long> tagged;
/**
* the ids of the subset of events that have at least one hash set hit
*/
private final ImmutableSet<Long> hashHits;
public EventStripe withParent(EventCluster parent) {
if (java.util.Objects.nonNull(this.parent)) {
throw new IllegalStateException("Event Stripe already has a parent!");
}
return new EventStripe(parent, this.type, this.description, this.lod, clusters, eventIDs, tagged, hashHits);
}
private EventStripe(EventCluster parent, EventType type, String description, DescriptionLoD lod, SortedSet<EventCluster> clusters, ImmutableSet<Long> eventIDs, ImmutableSet<Long> tagged, ImmutableSet<Long> hashHits) {
this.parent = parent;
this.type = type;
this.description = description;
this.lod = lod;
this.clusters = ImmutableSortedSet.copyOf(Comparator.comparing(EventCluster::getStartMillis), clusters);
this.eventIDs = eventIDs;
this.tagged = tagged;
this.hashHits = hashHits;
}
public EventStripe(EventCluster cluster) {
this.clusters = ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis))
.add(cluster.withParent(this)).build();
type = cluster.getEventType();
description = cluster.getDescription();
lod = cluster.getDescriptionLoD();
eventIDs = cluster.getEventIDs();
tagged = cluster.getEventIDsWithTags();
hashHits = cluster.getEventIDsWithHashHits();
this.parent = null;
}
private EventStripe(EventStripe u, EventStripe v) {
clusters = ImmutableSortedSet.orderedBy(Comparator.comparing(EventCluster::getStartMillis))
.addAll(u.getClusters())
.addAll(v.getClusters())
.build();
type = u.getEventType();
description = u.getDescription();
lod = u.getDescriptionLoD();
eventIDs = ImmutableSet.<Long>builder()
.addAll(u.getEventIDs())
.addAll(v.getEventIDs())
.build();
tagged = ImmutableSet.<Long>builder()
.addAll(u.getEventIDsWithTags())
.addAll(v.getEventIDsWithTags())
.build();
hashHits = ImmutableSet.<Long>builder()
.addAll(u.getEventIDsWithHashHits())
.addAll(v.getEventIDsWithHashHits())
.build();
parent = u.getParent().orElse(v.getParent().orElse(null));
}
@Override
public Optional<EventCluster> getParent() {
return Optional.ofNullable(parent);
}
public Optional<EventStripe> getParentStripe() {
if (getParent().isPresent()) {
return getParent().get().getParent();
} else {
return Optional.empty();
}
}
@Override
public String getDescription() {
return description;
}
@Override
public EventType getEventType() {
return type;
}
@Override
public DescriptionLoD getDescriptionLoD() {
return lod;
}
@Override
public ImmutableSet<Long> getEventIDs() {
return eventIDs;
}
@Override
public ImmutableSet<Long> getEventIDsWithHashHits() {
return hashHits;
}
@Override
public ImmutableSet<Long> getEventIDsWithTags() {
return tagged;
}
@Override
public long getStartMillis() {
return clusters.first().getStartMillis();
}
@Override
public long getEndMillis() {
return clusters.last().getEndMillis();
}
@Override
public ImmutableSortedSet< EventCluster> getClusters() {
return clusters;
}
@Override
public String toString() {
return "EventStripe{" + "description=" + description + ", eventIDs=" + (Objects.isNull(eventIDs) ? 0 : eventIDs.size()) + '}'; //NON-NLS
}
@Override
public int hashCode() {
int hash = 3;
hash = 79 * hash + Objects.hashCode(this.clusters);
hash = 79 * hash + Objects.hashCode(this.type);
hash = 79 * hash + Objects.hashCode(this.description);
hash = 79 * hash + Objects.hashCode(this.lod);
hash = 79 * hash + Objects.hashCode(this.eventIDs);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final EventStripe other = (EventStripe) obj;
if (!Objects.equals(this.description, other.description)) {
return false;
}
if (!Objects.equals(this.clusters, other.clusters)) {
return false;
}
if (!Objects.equals(this.type, other.type)) {
return false;
}
if (this.lod != other.lod) {
return false;
}
if (!Objects.equals(this.eventIDs, other.eventIDs)) {
return false;
}
return true;
}
}