All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.neko233.skilltree.game.redDot.impl.RedDotTreeByMemory Maven / Gradle / Ivy

There is a newer version: 0.3.6
Show newest version
package com.neko233.skilltree.game.redDot.impl;

import com.neko233.skilltree.annotation.Nullable;
import com.neko233.skilltree.commons.core.utils.ListUtils233;
import com.neko233.skilltree.commons.core.utils.MapUtils233;
import com.neko233.skilltree.game.redDot.RedDotTree;
import com.neko233.skilltree.game.redDot.dto.RedDotDto;
import com.neko233.skilltree.game.redDot.dto.RedPointRemoveResult;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 内存红点树
 */
public class RedDotTreeByMemory implements RedDotTree {

    // 根路径
    public static final String ROOT = "/";
    // 路径分隔符
    public static final String PATH_SEPARATOR = "/";

    // 
    private final Map pathToDataMap = new ConcurrentHashMap<>();

    public static RedDotTreeByMemory from(List redDotDtoList) {
        Map map = Optional.ofNullable(redDotDtoList).orElse(ListUtils233.of())
                .stream()
                .collect(Collectors.toMap(RedDotDto::getPath, Function.identity(), (v1, v2) -> v2));
        return new RedDotTreeByMemory(map);
    }

    public static RedDotTreeByMemory create() {
        return new RedDotTreeByMemory(null);
    }

    private RedDotTreeByMemory(Map map) {
        Map dataMap = map == null ? MapUtils233.of() : map;
        pathToDataMap.putAll(dataMap);
    }

    @Override
    public List addRedDot(String path,
                                  int value,
                                  long expireTimeMs) {
        long historyValue = getValueByRefresh(path);

        RedDotDto redDotDto = pathToDataMap.get(path);

        // 差异值
        long diffCount;
        if (redDotDto == null) {
            diffCount = value;
            redDotDto = createRedPointDto(path, expireTimeMs, value);
        } else {
            long targetValue = Math.max(historyValue + value, 0);
            diffCount = targetValue - historyValue;
            redDotDto.setCount(targetValue);
        }

        pathToDataMap.put(path, redDotDto);

        updateRedDotValue(path, diffCount);

        List allParentPathList = getAllParentPathList(path);
        allParentPathList.add(path);
        return allParentPathList;
    }

    @Nullable
    @Override
    public RedPointRemoveResult removeRedDot(String path) {
        final List removeChildPathList = getAllChildPathAndTargetList(path);
        final List updateParentPathList = getAllParentPathList(path);

        for (String removeChildPath : removeChildPathList) {
            RedDotDto removeObj = pathToDataMap.remove(removeChildPath);

            // 数据
            if (removeObj == null) {
                return null;
            }
            long removeValue = removeObj.getCount();
            long reduceValue = -removeValue;

            updateRedDotValue(path, reduceValue);
        }

        return RedPointRemoveResult.builder()
                .removeChildPathList(removeChildPathList)
                .updateParentPathList(updateParentPathList)
                .build();
    }

    @Override
    public List getAllParentPathList(String path) {
        if (StringUtils.isBlank(path)) {
            return ListUtils233.of();
        }
        List list = new ArrayList<>(2);
        String parentPath = getParentPath(path);
        while (!"/".equalsIgnoreCase(parentPath)) {
            list.add(parentPath);
            parentPath = getParentPath(parentPath);
        }
        return list;
    }

    @Override
    public long getRedDotValue(String path) {
        // 懒删除
        return getValueByRefresh(path);
    }

    /**
     * 是否过期了
     *
     * @param path 路径
     * @return ok ?
     */
    public boolean isExpire(String path) {
        RedDotDto redDotDto = pathToDataMap.get(path);

        if (redDotDto == null) {
            return false;
        }
        long currentMs = System.currentTimeMillis();
        return currentMs < redDotDto.getExpireMs();
    }

    public boolean isDataExpire(String path,
                                long currentMs) {
        RedDotDto redDotDto = pathToDataMap.get(path);
        if (redDotDto == null) {
            return false;
        }

        long expireTimeMs = redDotDto.getExpireMs();
        if (expireTimeMs >= 0 && currentMs > expireTimeMs) {

            return true;
        }
        return false;
    }

    /**
     * 会更新所有父级路径
     *
     * @param path       路径
     * @param childValue 子数据
     * @return toUpdatePathList
     */
    @Override
    public List updateRedDotValue(String path,
                                          long childValue) {
        String parentPath = getParentPath(path);

        if (parentPath != null) {
            RedDotDto redDotDto = pathToDataMap.get(parentPath);
            if (redDotDto == null) {
                // 不存在, 视为永久
                RedDotDto parentData = createRedPointDto(parentPath, -1L, childValue);
                pathToDataMap.put(parentData.getPath(), parentData);
            } else {
                long parentValue = redDotDto.getCount();
                redDotDto.setCount(parentValue + childValue);
                pathToDataMap.put(parentPath, redDotDto);
            }

            // "/" 根节点退出
            if (ROOT.equals(parentPath)) {
                return ListUtils233.of();
            }
            updateRedDotValue(parentPath, childValue);
        }

        return getAllParentPathAndTargetList(path);
    }

    private String getParentPath(String path) {
        int index = path.lastIndexOf('/');
        if (index == -1) {
            return null;
        }
        String parentPath = path.substring(0, index);
        return StringUtils.isBlank(parentPath) ? "/" : parentPath;
    }

    /**
     * 先执行懒删除后, 再获取
     *
     * @return 路径 : 数值 Map
     */
    @Override
    public Map getPathToValueMap() {
        long currentMs = System.currentTimeMillis();
        for (String path : pathToDataMap.keySet()) {
            boolean isExpire = isDataExpire(path, currentMs);
            if (isExpire) {
                removeRedDot(path);
            }
        }

        return pathToDataMap.values().stream()
                .collect(Collectors.toMap(RedDotDto::getPath, RedDotDto::getCount));
    }


    @Override
    public Map getAllDataMap() {
        return getAllDataMap(pathToDataMap.keySet());
    }


    @Override
    public Map getAllDataMap(Collection filterPathList) {
        return pathToDataMap.values()
                .stream()
                .filter(Objects::nonNull)
                .filter(data -> filterPathList.contains(data.getPath()))
                .collect(Collectors.toMap(RedDotDto::getPath, Function.identity(), (v1, v2) -> v2));
    }

    private static RedDotDto createRedPointDto(String path,
                                               Long expireTimeMs,
                                               long value) {
        long expireMs = Optional.ofNullable(expireTimeMs).orElse(RedDotTree.FOREVER_EXPIRE_MS);
        return RedDotDto.builder()
                .path(path)
                .count(value)
                .expireMs(expireMs)
                .build();
    }


    @Override
    public Long getExpireMsByPath(String path) {
        RedDotDto data = getRedDot(path);
        if (data == null) {
            return null;
        }
        return data.getExpireMs();
    }


    @Override
    public void clear() {
        pathToDataMap.clear();
    }

    @Override
    public RedDotDto getRedDot(String path) {
        return pathToDataMap.get(path);
    }

    @Override
    @Nullable
    public RedDotDto getByRefresh(String path) {
        // 所有子结构
        final List childPathList = getAllChildPathList(path);

        for (String childPath : childPathList) {
            getAndRemoveExpire(childPath);
        }

        RedDotDto redDotDto = getAndRemoveExpire(path);
        if (redDotDto == null) {
            return null;
        }
        return redDotDto;
    }

    @NotNull
    @Override
    public List getAllChildPathList(String path) {
        return pathToDataMap.keySet()
                .stream()
                // 不包含自己
                .filter(historyPath -> historyPath.startsWith(path + PATH_SEPARATOR))
                .collect(Collectors.toList());
    }

    @org.jetbrains.annotations.Nullable
    private RedDotDto getAndRemoveExpire(String path) {
        RedDotDto redDotDto = pathToDataMap.get(path);
        if (redDotDto == null) {
            return null;
        }

        boolean isExpire = isExpire(path);
        if (isExpire) {
            pathToDataMap.remove(path);
            return null;
        }
        return redDotDto;
    }


    @Override
    public List getAllRedDotList() {
        return new ArrayList<>(pathToDataMap.values());
    }

    @Override
    public void flush(List toUpdatePathList) {

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy