diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java index 9e9f0c58..38b7e118 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java @@ -26,9 +26,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.StreamTokenizer; import java.io.StringReader; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -56,6 +53,7 @@ import com.formdev.flatlaf.util.DerivedColor; import com.formdev.flatlaf.util.GrayFilter; import com.formdev.flatlaf.util.HSLColor; import com.formdev.flatlaf.util.LoggingFacade; +import com.formdev.flatlaf.util.SoftCache; import com.formdev.flatlaf.util.StringUtils; import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.UIScale; @@ -84,7 +82,7 @@ class UIDefaultsLoader private static int parseColorDepth; - private static final Cache fontCache = new Cache<>(); + private static final SoftCache fontCache = new SoftCache<>(); static void loadDefaultsFromProperties( Class lookAndFeelClass, List addons, Properties additionalDefaults, boolean dark, UIDefaults defaults ) @@ -1296,43 +1294,4 @@ class UIDefaultsLoader private static void throwMissingParametersException( String value ) { throw new IllegalArgumentException( "missing parameters in function '" + value + "'" ); } - - //---- class Cache -------------------------------------------------------- - - private static class Cache - { - private final Map> map = new HashMap<>(); - private final ReferenceQueue queue = new ReferenceQueue<>(); - - V get( K key ) { - expungeStaleEntries(); - CacheReference ref = map.get( key ); - return (ref != null) ? ref.get() : null; - } - - void put( K key, V value ) { - expungeStaleEntries(); - map.put( key, new CacheReference<>( key, value, queue ) ); - } - - @SuppressWarnings( "unchecked" ) - void expungeStaleEntries() { - Reference reference; - while( (reference = queue.poll()) != null ) - map.remove( ((CacheReference)reference).key ); - } - - //---- class CacheReference ---- - - private static class CacheReference - extends SoftReference - { - final K key; - - public CacheReference( K key, V value, ReferenceQueue queue ) { - super( value, queue ); - this.key = key; - } - } - } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/SoftCache.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/SoftCache.java new file mode 100644 index 00000000..c9f36546 --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/SoftCache.java @@ -0,0 +1,168 @@ +/* + * Copyright 2021 FormDev Software GmbH + * + * 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 + * + * https://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 com.formdev.flatlaf.util; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; + +/** + * A simple cache (map) that uses soft references for the values. + * + * @author Karl Tauber + * @since 2 + */ +public class SoftCache + implements Map +{ + private final Map> map; + private final ReferenceQueue queue = new ReferenceQueue<>(); + + public SoftCache() { + map = new HashMap<>(); + } + + public SoftCache( int initialCapacity ) { + map = new HashMap<>( initialCapacity ); + } + + @Override + public int size() { + expungeStaleEntries(); + return map.size(); + } + + @Override + public boolean isEmpty() { + expungeStaleEntries(); + return map.isEmpty(); + } + + @Override + public boolean containsKey( Object key ) { + expungeStaleEntries(); + return map.containsKey( key ); + } + + /** + * Not supported. Throws {@link UnsupportedOperationException}. + */ + @Override + public boolean containsValue( Object value ) { + throw new UnsupportedOperationException(); + } + + @Override + public V get( Object key ) { + expungeStaleEntries(); + return getRef( map.get( key ) ); + } + + @Override + public V put( K key, V value ) { + expungeStaleEntries(); + return getRef( map.put( key, new CacheReference<>( key, value, queue ) ) ); + } + + @Override + public V remove( Object key ) { + expungeStaleEntries(); + return getRef( map.remove( key ) ); + } + + private V getRef( CacheReference ref ) { + return (ref != null) ? ref.get() : null; + } + + @Override + public void putAll( Map m ) { + expungeStaleEntries(); + for( Entry e : m.entrySet() ) + put( e.getKey(), e.getValue() ); + } + + @Override + public void clear() { + map.clear(); + expungeStaleEntries(); + } + + @Override + public Set keySet() { + expungeStaleEntries(); + return map.keySet(); + } + + /** + * Not supported. Throws {@link UnsupportedOperationException}. + */ + @Override + public Collection values() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Throws {@link UnsupportedOperationException}. + */ + @Override + public Set> entrySet() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Throws {@link UnsupportedOperationException}. + */ + @Override + public void forEach( BiConsumer action ) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Throws {@link UnsupportedOperationException}. + */ + @Override + public void replaceAll( BiFunction function ) { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings( "unchecked" ) + private void expungeStaleEntries() { + Reference reference; + while( (reference = queue.poll()) != null ) + map.remove( ((CacheReference)reference).key ); + } + + //---- class CacheReference ---- + + private static class CacheReference + extends SoftReference + { + // needed to remove reference from map in expungeStaleEntries() + final K key; + + CacheReference( K key, V value, ReferenceQueue queue ) { + super( value, queue ); + this.key = key; + } + } +}