private static TimelineEntity maskFields(TimelineEntity entity, EnumSet<Field> fields) {
   // Conceal the fields that are not going to be exposed
   TimelineEntity entityToReturn = new TimelineEntity();
   entityToReturn.setEntityId(entity.getEntityId());
   entityToReturn.setEntityType(entity.getEntityType());
   entityToReturn.setStartTime(entity.getStartTime());
   // Deep copy
   if (fields.contains(Field.EVENTS)) {
     entityToReturn.addEvents(entity.getEvents());
   } else if (fields.contains(Field.LAST_EVENT_ONLY)) {
     entityToReturn.addEvent(entity.getEvents().get(0));
   } else {
     entityToReturn.setEvents(null);
   }
   if (fields.contains(Field.RELATED_ENTITIES)) {
     entityToReturn.addRelatedEntities(entity.getRelatedEntities());
   } else {
     entityToReturn.setRelatedEntities(null);
   }
   if (fields.contains(Field.PRIMARY_FILTERS)) {
     entityToReturn.addPrimaryFilters(entity.getPrimaryFilters());
   } else {
     entityToReturn.setPrimaryFilters(null);
   }
   if (fields.contains(Field.OTHER_INFO)) {
     entityToReturn.addOtherInfo(entity.getOtherInfo());
   } else {
     entityToReturn.setOtherInfo(null);
   }
   return entityToReturn;
 }
  @Test
  public void testEntities() throws Exception {
    TimelineEntities entities = new TimelineEntities();
    for (int j = 0; j < 2; ++j) {
      TimelineEntity entity = new TimelineEntity();
      entity.setEntityId("entity id " + j);
      entity.setEntityType("entity type " + j);
      entity.setStartTime(System.currentTimeMillis());
      for (int i = 0; i < 2; ++i) {
        TimelineEvent event = new TimelineEvent();
        event.setTimestamp(System.currentTimeMillis());
        event.setEventType("event type " + i);
        event.addEventInfo("key1", "val1");
        event.addEventInfo("key2", "val2");
        entity.addEvent(event);
      }
      entity.addRelatedEntity("test ref type 1", "test ref id 1");
      entity.addRelatedEntity("test ref type 2", "test ref id 2");
      entity.addPrimaryFilter("pkey1", "pval1");
      entity.addPrimaryFilter("pkey2", "pval2");
      entity.addOtherInfo("okey1", "oval1");
      entity.addOtherInfo("okey2", "oval2");
      entities.addEntity(entity);
    }
    LOG.info("Entities in JSON:");
    LOG.info(TimelineUtils.dumpTimelineRecordtoJSON(entities, true));

    Assert.assertEquals(2, entities.getEntities().size());
    TimelineEntity entity1 = entities.getEntities().get(0);
    Assert.assertEquals("entity id 0", entity1.getEntityId());
    Assert.assertEquals("entity type 0", entity1.getEntityType());
    Assert.assertEquals(2, entity1.getRelatedEntities().size());
    Assert.assertEquals(2, entity1.getEvents().size());
    Assert.assertEquals(2, entity1.getPrimaryFilters().size());
    Assert.assertEquals(2, entity1.getOtherInfo().size());
    TimelineEntity entity2 = entities.getEntities().get(1);
    Assert.assertEquals("entity id 1", entity2.getEntityId());
    Assert.assertEquals("entity type 1", entity2.getEntityType());
    Assert.assertEquals(2, entity2.getRelatedEntities().size());
    Assert.assertEquals(2, entity2.getEvents().size());
    Assert.assertEquals(2, entity2.getPrimaryFilters().size());
    Assert.assertEquals(2, entity2.getOtherInfo().size());
  }
 @Override
 public TimelineEvents getEntityTimelines(
     String entityType,
     SortedSet<String> entityIds,
     Long limit,
     Long windowStart,
     Long windowEnd,
     Set<String> eventTypes) {
   TimelineEvents allEvents = new TimelineEvents();
   if (entityIds == null) {
     return allEvents;
   }
   if (limit == null) {
     limit = DEFAULT_LIMIT;
   }
   if (windowStart == null) {
     windowStart = Long.MIN_VALUE;
   }
   if (windowEnd == null) {
     windowEnd = Long.MAX_VALUE;
   }
   for (String entityId : entityIds) {
     EntityIdentifier entityID = new EntityIdentifier(entityId, entityType);
     TimelineEntity entity = entities.get(entityID);
     if (entity == null) {
       continue;
     }
     EventsOfOneEntity events = new EventsOfOneEntity();
     events.setEntityId(entityId);
     events.setEntityType(entityType);
     for (TimelineEvent event : entity.getEvents()) {
       if (events.getEvents().size() >= limit) {
         break;
       }
       if (event.getTimestamp() <= windowStart) {
         continue;
       }
       if (event.getTimestamp() > windowEnd) {
         continue;
       }
       if (eventTypes != null && !eventTypes.contains(event.getEventType())) {
         continue;
       }
       events.addEvent(event);
     }
     allEvents.addEvent(events);
   }
   return allEvents;
 }
 @Override
 public TimelinePutResponse put(TimelineEntities data) {
   TimelinePutResponse response = new TimelinePutResponse();
   for (TimelineEntity entity : data.getEntities()) {
     EntityIdentifier entityId =
         new EntityIdentifier(entity.getEntityId(), entity.getEntityType());
     // store entity info in memory
     TimelineEntity existingEntity = entities.get(entityId);
     if (existingEntity == null) {
       existingEntity = new TimelineEntity();
       existingEntity.setEntityId(entity.getEntityId());
       existingEntity.setEntityType(entity.getEntityType());
       existingEntity.setStartTime(entity.getStartTime());
       entities.put(entityId, existingEntity);
       entityInsertTimes.put(entityId, System.currentTimeMillis());
     }
     if (entity.getEvents() != null) {
       if (existingEntity.getEvents() == null) {
         existingEntity.setEvents(entity.getEvents());
       } else {
         existingEntity.addEvents(entity.getEvents());
       }
       Collections.sort(existingEntity.getEvents());
     }
     // check startTime
     if (existingEntity.getStartTime() == null) {
       if (existingEntity.getEvents() == null || existingEntity.getEvents().isEmpty()) {
         TimelinePutError error = new TimelinePutError();
         error.setEntityId(entityId.getId());
         error.setEntityType(entityId.getType());
         error.setErrorCode(TimelinePutError.NO_START_TIME);
         response.addError(error);
         entities.remove(entityId);
         entityInsertTimes.remove(entityId);
         continue;
       } else {
         Long min = Long.MAX_VALUE;
         for (TimelineEvent e : entity.getEvents()) {
           if (min > e.getTimestamp()) {
             min = e.getTimestamp();
           }
         }
         existingEntity.setStartTime(min);
       }
     }
     if (entity.getPrimaryFilters() != null) {
       if (existingEntity.getPrimaryFilters() == null) {
         existingEntity.setPrimaryFilters(new HashMap<String, Set<Object>>());
       }
       for (Entry<String, Set<Object>> pf : entity.getPrimaryFilters().entrySet()) {
         for (Object pfo : pf.getValue()) {
           existingEntity.addPrimaryFilter(pf.getKey(), maybeConvert(pfo));
         }
       }
     }
     if (entity.getOtherInfo() != null) {
       if (existingEntity.getOtherInfo() == null) {
         existingEntity.setOtherInfo(new HashMap<String, Object>());
       }
       for (Entry<String, Object> info : entity.getOtherInfo().entrySet()) {
         existingEntity.addOtherInfo(info.getKey(), maybeConvert(info.getValue()));
       }
     }
     // relate it to other entities
     if (entity.getRelatedEntities() == null) {
       continue;
     }
     for (Map.Entry<String, Set<String>> partRelatedEntities :
         entity.getRelatedEntities().entrySet()) {
       if (partRelatedEntities == null) {
         continue;
       }
       for (String idStr : partRelatedEntities.getValue()) {
         EntityIdentifier relatedEntityId =
             new EntityIdentifier(idStr, partRelatedEntities.getKey());
         TimelineEntity relatedEntity = entities.get(relatedEntityId);
         if (relatedEntity != null) {
           relatedEntity.addRelatedEntity(
               existingEntity.getEntityType(), existingEntity.getEntityId());
         } else {
           relatedEntity = new TimelineEntity();
           relatedEntity.setEntityId(relatedEntityId.getId());
           relatedEntity.setEntityType(relatedEntityId.getType());
           relatedEntity.setStartTime(existingEntity.getStartTime());
           relatedEntity.addRelatedEntity(
               existingEntity.getEntityType(), existingEntity.getEntityId());
           entities.put(relatedEntityId, relatedEntity);
           entityInsertTimes.put(relatedEntityId, System.currentTimeMillis());
         }
       }
     }
   }
   return response;
 }