Shemplo/Neerc-chat-client

View on GitHub
src/main/java/ru/shemplo/chat/neerc/network/TasksService.java

Summary

Maintainability
A
1 hr
Test Coverage
package ru.shemplo.chat.neerc.network;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import org.jivesoftware.smack.util.StringUtils;

import ru.shemplo.chat.neerc.config.ConfigStorage;
import ru.shemplo.chat.neerc.enities.MessageEntity;
import ru.shemplo.chat.neerc.enities.MessageEntity.MessageAccess;
import ru.shemplo.chat.neerc.enities.TaskEntity;
import ru.shemplo.chat.neerc.network.iq.CustomIQProvider;
import ru.shemplo.chat.neerc.network.listeners.TasksStatusListener;
import ru.shemplo.snowball.annot.Snowflake;

@Snowflake
public class TasksService {
    
    private CustomIQProvider customIQProvider;
    private MessageService messageService;
    private ConfigStorage configStorage;
    private UsersService usersService;
    
    private final Object STUB_OBJECT = new Object ();
    private final ConcurrentMap <TasksStatusListener, Object> 
        listeners = new ConcurrentHashMap <> ();
    
    public void subscribe (final TasksStatusListener listener) {
        listeners.computeIfAbsent (listener, k -> {
            listener.onTasksUpdated ();
            return STUB_OBJECT;
        });
    }
    
    private final Map <String, TaskEntity> 
        tasks = new ConcurrentHashMap <> ();
    
    public synchronized void mergeTasks (Collection <TaskEntity> tasks) {        
        boolean numberOfTasksChanged = this.tasks.size () != tasks.size ();
        List <String> keys = new ArrayList <> (this.tasks.keySet ());
        Set <String> keep = new HashSet <> (tasks.stream ()
                          . map (TaskEntity::getId)
                          . collect (Collectors.toSet ()));
        keep.retainAll (this.tasks.keySet ());
        keys.stream ().filter (k -> !keep.contains (k))
            .forEach (k -> {
                MessageEntity message = prepareMessage ("", "deleted", 
                                      this.tasks.get (k).getTitle ());
                messageService.addMessage (message);
                this.tasks.remove (k);
            });
        
        AtomicBoolean hasJustAssignedTask = new AtomicBoolean (false);
        tasks.forEach (task -> {
            this.tasks.compute (task.getId (), (k, v) -> {
                if (v == null && task.getStatuses ().size () == 0) {
                    MessageEntity message = prepareMessage ("", 
                                  "created", task.getTitle ());
                    messageService.addMessage (message);
                    
                    hasJustAssignedTask.set (true);
                    return task;
                } else if (v == null || v.getStatuses ().size () == 0) {
                    final String user = configStorage.get ("login")
                                      . orElse ("[user name]");
                    task.getStatusFor (user).ifPresent (__ -> {                        
                        MessageEntity message = prepareMessage ("!!", 
                                "assigned to you", task.getTitle ());
                        messageService.addMessage (message);
                    });
                    
                    hasJustAssignedTask.set (true);
                }
                
                // TODO: hash compare
                
                return task;
            });
        });
        
        if (numberOfTasksChanged || hasJustAssignedTask.get ()) {
            listeners.keySet ().forEach (TasksStatusListener::onTasksUpdated);
        } else {
            tasks.forEach (task -> {
                listeners.keySet ().forEach (lis -> 
                    lis.onTaskUpdated (task.getId ()));
            });
        }
    }
    
    private final MessageEntity prepareMessage (String level, String action, String title) {
        final String ID        = StringUtils.randomString (16),
                author    = usersService.isUser ("tk") ? "tk" : "tc", // technical committee
                recipient = configStorage.get ("login").orElse ("[user name]"),
                body      = String.format ("%sTask was %s: %s", 
                                           level, action, title);
        final LocalDateTime time   = LocalDateTime.now ();
        final MessageAccess access = MessageAccess.ROOM_PRIVATE;
        MessageEntity message = new MessageEntity ("tasks", ID, 
                        time, author, recipient, body, access);
        return message;
    }
    
    public Collection <TaskEntity> getActualTasksFor (String name) {
        return tasks.values ().stream ()
             . filter  (task -> task.isAssignedTo (name))
             . collect (Collectors.toList ());
    }
    
    public Optional <TaskEntity> getTaskByID (String id) {
        return Optional.ofNullable (tasks.get (id));
    }
    
    public void onTaskChanged (String taskID) {
        System.out.println ("Task `" + taskID + "` changed");
        customIQProvider.query ("tasks");
    }
    
}