net.dv8tion.jda.internal.utils.cache.ChannelCacheViewImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of JDA Show documentation
Show all versions of JDA Show documentation
Java wrapper for the popular chat & VOIP service: Discord https://discord.com
The newest version!
/*
* Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
*
* 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 net.dv8tion.jda.internal.utils.cache;
import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import net.dv8tion.jda.api.entities.channel.Channel;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.utils.ClosableIterator;
import net.dv8tion.jda.api.utils.LockIterator;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.api.utils.cache.ChannelCacheView;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.Helpers;
import net.dv8tion.jda.internal.utils.UnlockHook;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ChannelCacheViewImpl extends ReadWriteLockCache implements ChannelCacheView
{
protected final EnumMap> caches = new EnumMap<>(ChannelType.class);
public ChannelCacheViewImpl(Class type)
{
for (ChannelType channelType : ChannelType.values())
{
channelType = normalizeKey(channelType);
Class extends Channel> clazz = channelType.getInterface();
if (channelType != ChannelType.UNKNOWN && type.isAssignableFrom(clazz))
caches.put(channelType, new TLongObjectHashMap<>());
}
}
// Store all threads under the same channel type, makes it easier because the interface is shared
protected ChannelType normalizeKey(ChannelType type)
{
return type.isThread() ? ChannelType.GUILD_PUBLIC_THREAD : type;
}
@Nullable
@SuppressWarnings("unchecked")
protected TLongObjectMap getMap(@Nonnull ChannelType type)
{
return (TLongObjectMap) caches.get(normalizeKey(type));
}
@Nullable
@SuppressWarnings("unchecked")
public C put(C element)
{
try (UnlockHook hook = writeLock())
{
return (C) getMap(element.getType()).put(element.getIdLong(), element);
}
}
@Nullable
@SuppressWarnings("unchecked")
public C remove(ChannelType type, long id)
{
try (UnlockHook hook = writeLock())
{
T removed = getMap(type).remove(id);
return (C) removed;
}
}
public C remove(C channel)
{
return remove(channel.getType(), channel.getIdLong());
}
public void removeIf(Class typeFilter, Predicate super C> predicate)
{
try (UnlockHook hook = writeLock())
{
ofType(typeFilter).removeIf(predicate);
}
}
public void clear()
{
try (UnlockHook hook = writeLock())
{
caches.values().forEach(TLongObjectMap::clear);
}
}
@Nonnull
@Override
public FilteredCacheView ofType(@Nonnull Class type)
{
return new FilteredCacheView<>(type);
}
@Override
public void forEach(Consumer super T> action)
{
try (UnlockHook hook = readLock())
{
for (TLongObjectMap cache : caches.values())
{
cache.valueCollection().forEach(action);
}
}
}
@Nonnull
@Override
public List asList()
{
List list = getCachedList();
if (list == null)
list = cache((List) applyStream(stream -> stream.collect(Collectors.toList())));
return list;
}
@Nonnull
@Override
public Set asSet()
{
Set set = getCachedSet();
if (set == null)
set = cache((Set) applyStream(stream -> stream.collect(Collectors.toSet())));
return set;
}
@Nonnull
@Override
public ClosableIterator lockedIterator()
{
ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
MiscUtil.tryLock(readLock);
try
{
Iterator extends T> directIterator = caches.values()
.stream()
.flatMap(map -> map.valueCollection().stream())
.iterator();
return new LockIterator<>(directIterator, readLock);
}
catch (Throwable t)
{
readLock.unlock();
throw t;
}
}
@Override
public long size()
{
try (UnlockHook hook = readLock())
{
return caches.values().stream().mapToLong(TLongObjectMap::size).sum();
}
}
@Override
public boolean isEmpty()
{
try (UnlockHook hook = readLock())
{
return caches.values().stream().allMatch(TLongObjectMap::isEmpty);
}
}
@Nonnull
@Override
public List getElementsByName(@Nonnull String name, boolean ignoreCase)
{
Checks.notEmpty(name, "Name");
return applyStream(stream ->
stream
.filter((channel) -> Helpers.equals(channel.getName(), name, ignoreCase))
.collect(Helpers.toUnmodifiableList())
);
}
@Nonnull
@Override
public Stream stream()
{
return this.asList().stream();
}
@Nonnull
@Override
public Stream parallelStream()
{
return this.asList().parallelStream();
}
@Nullable
@Override
public T getElementById(long id)
{
try (UnlockHook hook = readLock())
{
for (TLongObjectMap extends T> cache : caches.values())
{
T element = cache.get(id);
if (element != null)
return element;
}
return null;
}
}
public T getElementById(@Nonnull ChannelType type, long id)
{
Checks.notNull(type, "ChannelType");
try (UnlockHook hook = readLock())
{
TLongObjectMap map = getMap(type);
return map == null ? null : map.get(id);
}
}
@Nonnull
@Override
public Iterator iterator()
{
return stream().iterator();
}
public class FilteredCacheView implements ChannelCacheView
{
protected final Class type;
protected final List> filteredMaps;
@SuppressWarnings("unchecked")
protected FilteredCacheView(Class type)
{
Checks.notNull(type, "Type");
this.type = type;
this.filteredMaps = caches.entrySet()
.stream()
.filter(entry -> entry.getKey() != null && type.isAssignableFrom(entry.getKey().getInterface()))
.map(entry -> (TLongObjectMap) entry.getValue())
.collect(Collectors.toList());
}
protected void removeIf(Predicate super C> filter)
{
this.filteredMaps.forEach(map -> map.valueCollection().removeIf(filter));
}
@Nonnull
@Override
public List asList()
{
return applyStream(stream -> stream.collect(Helpers.toUnmodifiableList()));
}
@Nonnull
@Override
public Set asSet()
{
return applyStream(stream ->
stream.collect(
Collectors.collectingAndThen(
Collectors.toSet(),
Collections::unmodifiableSet))
);
}
@Nonnull
@Override
@SuppressWarnings("unchecked")
public ClosableIterator lockedIterator()
{
ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
MiscUtil.tryLock(readLock);
try
{
Iterator extends C> directIterator =filteredMaps
.stream()
.flatMap(map -> map.valueCollection().stream())
.iterator();
return new LockIterator<>(directIterator, readLock);
}
catch (Throwable t)
{
readLock.unlock();
throw t;
}
}
@Override
public long size()
{
try (UnlockHook hook = readLock())
{
return filteredMaps
.stream()
.mapToLong(TLongObjectMap::size)
.sum();
}
}
@Override
public boolean isEmpty()
{
try (UnlockHook hook = readLock())
{
return filteredMaps
.stream()
.allMatch(TLongObjectMap::isEmpty);
}
}
@Nonnull
@Override
public List getElementsByName(@Nonnull String name, boolean ignoreCase)
{
Checks.notEmpty(name, "Name");
return applyStream(stream ->
stream
.filter(channel -> Helpers.equals(channel.getName(), name, ignoreCase))
.collect(Helpers.toUnmodifiableList())
);
}
@Nonnull
@Override
public Stream stream()
{
return asList().stream();
}
@Nonnull
@Override
public Stream parallelStream()
{
return asList().parallelStream();
}
@Nonnull
@Override
public ChannelCacheView ofType(@Nonnull Class type)
{
return ChannelCacheViewImpl.this.ofType(type);
}
@Nullable
@Override
public C getElementById(@Nonnull ChannelType type, long id)
{
T channel = ChannelCacheViewImpl.this.getElementById(type, id);
return this.type.isInstance(channel) ? this.type.cast(channel) : null;
}
@Nullable
@Override
public C getElementById(long id)
{
try (UnlockHook hook = readLock())
{
return filteredMaps
.stream()
.map(it -> it.get(id))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
}
@Nonnull
@Override
public Iterator iterator()
{
return asList().iterator();
}
}
}