/*
 * Copyright 2020 the original author or authors.
 *
 * 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.gradle.cache.internal;

import com.google.common.collect.Lists;

import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;

public class CleaningInMemoryCacheDecoratorFactory extends DefaultInMemoryCacheDecoratorFactory {
    private final List<WeakReference<InMemoryCacheController>> inMemoryCaches = Lists.newArrayList();

    public CleaningInMemoryCacheDecoratorFactory(boolean longLivingProcess, CrossBuildInMemoryCacheFactory cacheFactory) {
        super(longLivingProcess, cacheFactory);
    }

    public void clearCaches(Predicate<InMemoryCacheController> predicate) {
        for (Iterator<WeakReference<InMemoryCacheController>> iterator = inMemoryCaches.iterator(); iterator.hasNext();) {
            WeakReference<InMemoryCacheController> ref = iterator.next();
            InMemoryCacheController cache = ref.get();
            if (cache == null) {
                iterator.remove();
            } else if (predicate.test(cache)) {
                cache.clearInMemoryCache();
            }
        }
    }

    @Override
    protected <K, V> MultiProcessSafeAsyncPersistentIndexedCache<K, V> applyInMemoryCaching(String cacheId, MultiProcessSafeAsyncPersistentIndexedCache<K, V> backingCache, int maxEntriesToKeepInMemory, boolean cacheInMemoryForShortLivedProcesses) {
        MultiProcessSafeAsyncPersistentIndexedCache<K, V> delegate = super.applyInMemoryCaching(cacheId, backingCache, maxEntriesToKeepInMemory, cacheInMemoryForShortLivedProcesses);
        if (delegate instanceof InMemoryCacheController) {
            InMemoryCacheController cimc = (InMemoryCacheController) delegate;
            inMemoryCaches.add(new WeakReference<>(cimc));
        }
        return delegate;
    }
}

@@ -1261,24 +1261,68 @@
 redicate) %7B%0A
+        synchronized (inMemoryCaches) %7B%0A    
         for 
@@ -1444,16 +1444,20 @@
         
+    
 WeakRefe
@@ -1514,32 +1514,36 @@
 ();%0A            
+ 
+   
 InMemoryCacheCon
@@ -1577,24 +1577,28 @@
             
+  
+  
 if (cache ==
@@ -1602,24 +1602,28 @@
  == null) %7B%0A
+    
             
@@ -1657,16 +1657,20 @@
         
+    
 %7D else i
@@ -1713,16 +1713,20 @@
         
+ 
+   
 cache.cl
@@ -1741,24 +1741,42 @@
 oryCache();%0A
+                %7D%0A
             
@@ -2394,54 +2394,178 @@
     
-inMemoryCaches.add(new WeakReference%3C%3E(cimc));
+WeakReference%3CInMemoryCacheController%3E ref = new WeakReference%3C%3E(cimc);%0A            synchronized (inMemoryCaches) %7B%0A                inMemoryCaches.add(ref);%0A            %7D
 %0A   

Fix potential NPE if code is executed concurrently