From 896e9bca8e78276e3da86796a41375091eb6108c Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Wed, 11 Aug 2021 21:53:10 +0200 Subject: [PATCH] Theme Editor: re-implemented support loading/resolving base properties from other editors in opened directory --- .../themeeditor/FlatThemeEditorPane.java | 7 +- .../themeeditor/FlatThemeFileEditor.java | 5 + .../FlatThemePropertiesBaseManager.java | 160 ++++++++++++++++++ .../FlatThemePropertiesSupport.java | 97 +++++------ 4 files changed, 218 insertions(+), 51 deletions(-) create mode 100644 flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemePropertiesBaseManager.java diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeEditorPane.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeEditorPane.java index 8bc22b38..5f30b239 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeEditorPane.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeEditorPane.java @@ -23,7 +23,6 @@ import java.awt.Window; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.List; import javax.swing.BorderFactory; import javax.swing.InputMap; import javax.swing.JComponent; @@ -49,6 +48,7 @@ import org.fife.ui.rsyntaxtextarea.TokenTypes; import org.fife.ui.rtextarea.Gutter; import org.fife.ui.rtextarea.RTextArea; import org.fife.ui.rtextarea.RTextScrollPane; +import com.formdev.flatlaf.util.StringUtils; import com.formdev.flatlaf.util.UIScale; /** @@ -168,8 +168,9 @@ class FlatThemeEditorPane return textArea.requestFocusInWindow(); } - void setBaseFiles( List baseFiles ) { - textArea.propertiesSupport.setBaseFiles( baseFiles ); + void initBasePropertyProvider( FlatThemePropertiesBaseManager propertiesBaseManager ) { + String name = StringUtils.removeTrailing( file.getName(), ".properties" ); + textArea.propertiesSupport.setBasePropertyProvider( propertiesBaseManager.create( name, textArea.propertiesSupport ) ); } File getFile() { diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java index 37ac305d..704e7e75 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemeFileEditor.java @@ -68,6 +68,8 @@ public class FlatThemeFileEditor private Preferences state; private boolean inLoadDirectory; + private final FlatThemePropertiesBaseManager propertiesBaseManager = new FlatThemePropertiesBaseManager(); + public static void main( String[] args ) { File dir = (args.length > 0) ? new File( args[0] ) @@ -187,6 +189,7 @@ public class FlatThemeFileEditor return; this.dir = dir; + propertiesBaseManager.clear(); inLoadDirectory = true; @@ -266,6 +269,8 @@ public class FlatThemeFileEditor ex.printStackTrace(); // TODO } + themeEditorPane.initBasePropertyProvider( propertiesBaseManager ); + Supplier titleFun = () -> { return (themeEditorPane.isDirty() ? "* " : "") + StringUtils.removeTrailing( themeEditorPane.getFile().getName(), ".properties" ); diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemePropertiesBaseManager.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemePropertiesBaseManager.java new file mode 100644 index 00000000..ab35e831 --- /dev/null +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemePropertiesBaseManager.java @@ -0,0 +1,160 @@ +/* + * 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.themeeditor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * @author Karl Tauber + */ +class FlatThemePropertiesBaseManager +{ + private final Map providers = new HashMap<>(); + + FlatThemePropertiesSupport.BasePropertyProvider create( String name, FlatThemePropertiesSupport propertiesSupport ) { + MyBasePropertyProvider provider = new MyBasePropertyProvider( name, propertiesSupport ); + providers.put( name, provider ); + return provider; + } + + void clear() { + providers.clear(); + } + + private static List baseFiles( String name, String baseTheme ) { + ArrayList result = new ArrayList<>(); + + // core themes + switch( name ) { + case "FlatLaf": + result.add( "FlatLightLaf" ); + break; + + case "FlatLightLaf": + case "FlatDarkLaf": + result.add( "FlatLaf" ); + break; + + case "FlatIntelliJLaf": + result.add( "FlatLightLaf" ); + result.add( "FlatLaf" ); + break; + + case "FlatDarculaLaf": + result.add( "FlatDarkLaf" ); + result.add( "FlatLaf" ); + break; + } + + // custom themes based on core themes + if( result.isEmpty() ) { + if( baseTheme == null ) + baseTheme = "light"; + + switch( baseTheme ) { + default: + case "light": + result.add( "FlatLightLaf" ); + result.add( "FlatLaf" ); + break; + + case "dark": + result.add( "FlatDarkLaf" ); + result.add( "FlatLaf" ); + break; + + case "intellij": + result.add( "FlatIntelliJLaf" ); + result.add( "FlatLightLaf" ); + result.add( "FlatLaf" ); + break; + + case "darcula": + result.add( "FlatDarculaLaf" ); + result.add( "FlatLightLaf" ); + result.add( "FlatLaf" ); + break; + } + } + + return result; + } + + //---- class MyBasePropertyProvider --------------------------------------- + + private class MyBasePropertyProvider + implements FlatThemePropertiesSupport.BasePropertyProvider + { + private final String name; + private final FlatThemePropertiesSupport propertiesSupport; + + private List baseFiles; + private String lastBaseTheme; + + MyBasePropertyProvider( String name, FlatThemePropertiesSupport propertiesSupport ) { + this.name = name; + this.propertiesSupport = propertiesSupport; + } + + @Override + public String getProperty( String key, String baseTheme ) { + updateBaseFiles( baseTheme ); + + for( String baseFile : baseFiles ) { + String value = getPropertyFromBase( baseFile, key ); + if( value != null ) + return value; + } + + return null; + } + + private String getPropertyFromBase( String baseFile, String key ) { + MyBasePropertyProvider provider = providers.get( baseFile ); + return (provider != null) + ? provider.propertiesSupport.getProperties().getProperty( key ) + : null; + } + + private void updateBaseFiles( String baseTheme ) { + if( baseFiles != null && Objects.equals( baseTheme, lastBaseTheme ) ) + return; + + baseFiles = baseFiles( name, baseTheme ); + lastBaseTheme = baseTheme; + } + + @Override + public void addAllKeys( Set allKeys, String baseTheme ) { + updateBaseFiles( baseTheme ); + + for( String baseFile : baseFiles ) { + MyBasePropertyProvider provider = providers.get( baseFile ); + if( provider == null ) + continue; + + for( Object key : provider.propertiesSupport.getProperties().keySet() ) + allKeys.add( (String) key ); + } + } + } +} diff --git a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemePropertiesSupport.java b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemePropertiesSupport.java index 163c6fa2..5b98a264 100644 --- a/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemePropertiesSupport.java +++ b/flatlaf-theme-editor/src/main/java/com/formdev/flatlaf/themeeditor/FlatThemePropertiesSupport.java @@ -17,14 +17,10 @@ package com.formdev.flatlaf.themeeditor; import java.awt.Color; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.io.StringReader; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; @@ -45,14 +41,16 @@ class FlatThemePropertiesSupport private final FlatSyntaxTextArea textArea; private final Function propertiesGetter; private final Function resolver; + private BasePropertyProvider basePropertyProvider; + + // caches private Properties propertiesCache; private final Map parsedValueCache = new HashMap<>(); - - private File[] baseFiles; - private long[] baseFilesLastModified; - private Properties[] basePropertiesCache; - private Set allKeysCache; + private String baseTheme; + + private static long globalCacheInvalidationCounter; + private long cacheInvalidationCounter; FlatThemePropertiesSupport( FlatSyntaxTextArea textArea ) { this.textArea = textArea; @@ -67,12 +65,8 @@ class FlatThemePropertiesSupport textArea.getDocument().addDocumentListener( this ); } - void setBaseFiles( List baseFiles ) { - int size = baseFiles.size(); - this.baseFiles = baseFiles.toArray( new File[size] ); - - baseFilesLastModified = new long[size]; - basePropertiesCache = new Properties[size]; + void setBasePropertyProvider( BasePropertyProvider basePropertyProvider ) { + this.basePropertyProvider = basePropertyProvider; } private String resolveValue( String value ) { @@ -80,6 +74,8 @@ class FlatThemePropertiesSupport } Object getParsedValueAtLine( int line ) { + autoClearCache(); + Integer lineKey = line; Object parsedValue = parsedValueCache.get( lineKey ); if( parsedValue != null ) @@ -96,7 +92,7 @@ class FlatThemePropertiesSupport parsedValueCache.put( lineKey, parsedValue ); return parsedValue; } catch( Exception ex ) { - System.out.println( ex.getMessage() ); //TODO + System.out.println( textArea.getFileName() + ": " + ex.getMessage() ); //TODO parsedValueCache.put( lineKey, ex ); return null; } @@ -128,20 +124,14 @@ class FlatThemePropertiesSupport if( value != null ) return value; - if( baseFiles == null ) + if( basePropertyProvider == null ) return null; // look in base properties files - for( int i = 0; i < baseFiles.length; i++ ) { - value = getBaseProperties( i ).getProperty( key ); - if( value != null ) - return value; - } - - return null; + return basePropertyProvider.getProperty( key, getBaseTheme() ); } - private Properties getProperties() { + Properties getProperties() { if( propertiesCache != null ) return propertiesCache; @@ -154,23 +144,9 @@ class FlatThemePropertiesSupport return propertiesCache; } - private Properties getBaseProperties( int index ) { - long lastModified = baseFiles[index].lastModified(); - if( baseFilesLastModified[index] != lastModified || basePropertiesCache[index] == null ) { - // (re)load base properties file - baseFilesLastModified[index] = lastModified; - basePropertiesCache[index] = new Properties(); - try( InputStream in = new FileInputStream( baseFiles[index] ) ) { - basePropertiesCache[index].load( in ); - } catch( IOException ex ) { - ex.printStackTrace(); //TODO - } - } - - return basePropertiesCache[index]; - } - Set getAllKeys() { + autoClearCache(); + if( allKeysCache != null ) return allKeysCache; @@ -179,21 +155,39 @@ class FlatThemePropertiesSupport for( Object key : getProperties().keySet() ) allKeysCache.add( (String) key ); - if( baseFiles == null ) - return allKeysCache; - - for( int i = 0; i < baseFiles.length; i++ ) { - for( Object key : getBaseProperties( i ).keySet() ) - allKeysCache.add( (String) key ); - } + // look in base properties files + if( basePropertyProvider != null ) + basePropertyProvider.addAllKeys( allKeysCache, getBaseTheme() ); return allKeysCache; } + private String getBaseTheme() { + if( baseTheme == null ) + baseTheme = getProperties().getProperty( "@baseTheme", "light" ); + return baseTheme; + } + private void clearCache() { propertiesCache = null; parsedValueCache.clear(); allKeysCache = null; + baseTheme = null; + + // increase global cache invalidation counter to allow auto-clear caches + globalCacheInvalidationCounter++; + cacheInvalidationCounter = globalCacheInvalidationCounter; + } + + /** + * Clear caches that may depend on other editors if cache of another editor was invalidated. + */ + private void autoClearCache() { + if( cacheInvalidationCounter == globalCacheInvalidationCounter ) + return; + + parsedValueCache.clear(); + allKeysCache = null; } //---- interface DocumentListener ---- @@ -233,4 +227,11 @@ class FlatThemePropertiesSupport this.value = value; } } + + //---- interface BasePropertyProvider ------------------------------------- + + interface BasePropertyProvider { + String getProperty( String key, String baseTheme ); + void addAllKeys( Set allKeys, String baseTheme ); + } }