Styling: support specifying explicit value type for parsing CSS values (for future use)

This commit is contained in:
Karl Tauber
2021-08-31 23:53:12 +02:00
parent b12c818862
commit 6f9bbb184a
6 changed files with 192 additions and 67 deletions

View File

@@ -784,14 +784,17 @@ public abstract class FlatLaf
* <p>
* See: <a href="https://www.formdev.com/flatlaf/properties-files/">https://www.formdev.com/flatlaf/properties-files/</a>
*
* @param key the key, which is used to determine the value type
* @param key the key, which is used to determine the value type if parameter {@code valueType} is {@code null}
* @param value the value string
* @param valueType the expected value type, or {@code null}
* @return the binary value
* @throws IllegalArgumentException on syntax errors
* @since TODO
*/
public static Object parseDefaultsValue( String key, String value ) throws IllegalArgumentException {
return UIDefaultsLoader.parseValue( key, value );
public static Object parseDefaultsValue( String key, String value, Class<?> valueType )
throws IllegalArgumentException
{
return UIDefaultsLoader.parseValue( key, value, valueType );
}
private static void reSetLookAndFeel() {

View File

@@ -338,7 +338,7 @@ public class IntelliJTheme
// parse value
try {
uiValue = UIDefaultsLoader.parseValue( key, valueStr );
uiValue = UIDefaultsLoader.parseValue( key, valueStr, null );
} catch( RuntimeException ex ) {
UIDefaultsLoader.logParseError( key, valueStr, ex, false );
return; // ignore invalid value

View File

@@ -33,8 +33,10 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.function.Function;
import javax.swing.Icon;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.UIDefaults.ActiveValue;
import javax.swing.UIDefaults.LazyValue;
import javax.swing.plaf.ColorUIResource;
@@ -241,7 +243,7 @@ class UIDefaultsLoader
String value = resolveValue( (String) e.getValue(), propertiesGetter );
try {
defaults.put( key, parseValue( key, value, null, resolver, addonClassLoaders ) );
defaults.put( key, parseValue( key, value, null, null, resolver, addonClassLoaders ) );
} catch( RuntimeException ex ) {
logParseError( key, value, ex, true );
}
@@ -292,12 +294,13 @@ class UIDefaultsLoader
SCALEDINTEGER, SCALEDFLOAT, SCALEDINSETS, SCALEDDIMENSION, INSTANCE, CLASS, GRAYFILTER, NULL, LAZY }
private static ValueType[] tempResultValueType = new ValueType[1];
private static Map<Class<?>, ValueType> javaValueTypes;
static Object parseValue( String key, String value ) {
return parseValue( key, value, null, v -> v, Collections.emptyList() );
static Object parseValue( String key, String value, Class<?> valueType ) {
return parseValue( key, value, valueType, null, v -> v, Collections.emptyList() );
}
static Object parseValue( String key, String value, ValueType[] resultValueType,
static Object parseValue( String key, String value, Class<?> javaValueType, ValueType[] resultValueType,
Function<String, String> resolver, List<ClassLoader> addonClassLoaders )
{
if( resultValueType == null )
@@ -305,71 +308,106 @@ class UIDefaultsLoader
value = value.trim();
// null, false, true
switch( value ) {
case "null": resultValueType[0] = ValueType.NULL; return null;
case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
}
// check for function "lazy"
// Syntax: lazy(uiKey)
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
resultValueType[0] = ValueType.LAZY;
String uiKey = value.substring( 5, value.length() - 1 ).trim();
return (LazyValue) t -> {
return lazyUIManagerGet( uiKey );
};
// null
if( value.equals( "null" ) ) {
resultValueType[0] = ValueType.NULL;
return null;
}
ValueType valueType = ValueType.UNKNOWN;
// check whether value type is specified in the value
if( value.startsWith( "#" ) )
valueType = ValueType.COLOR;
else if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
valueType = ValueType.STRING;
value = value.substring( 1, value.length() - 1 );
} else if( value.startsWith( TYPE_PREFIX ) ) {
int end = value.indexOf( TYPE_PREFIX_END );
if( end != -1 ) {
try {
String typeStr = value.substring( TYPE_PREFIX.length(), end );
valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
if( javaValueType != null ) {
if( javaValueTypes == null ) {
// create lazy
javaValueTypes = new HashMap<>();
javaValueTypes.put( String.class, ValueType.STRING );
javaValueTypes.put( boolean.class, ValueType.BOOLEAN );
javaValueTypes.put( Boolean.class, ValueType.BOOLEAN );
javaValueTypes.put( char.class, ValueType.CHARACTER );
javaValueTypes.put( Character.class, ValueType.CHARACTER );
javaValueTypes.put( int.class, ValueType.INTEGER );
javaValueTypes.put( Integer.class, ValueType.INTEGER );
javaValueTypes.put( float.class, ValueType.FLOAT );
javaValueTypes.put( Float.class, ValueType.FLOAT );
javaValueTypes.put( Border.class, ValueType.BORDER );
javaValueTypes.put( Icon.class, ValueType.ICON );
javaValueTypes.put( Insets.class, ValueType.INSETS );
javaValueTypes.put( Dimension.class, ValueType.DIMENSION );
javaValueTypes.put( Color.class, ValueType.COLOR );
}
// remove type from value
value = value.substring( end + TYPE_PREFIX_END.length() );
} catch( IllegalArgumentException ex ) {
// ignore
// map java value type to parser value type
valueType = javaValueTypes.get( javaValueType );
if( valueType == null )
throw new IllegalArgumentException( "unsupported value type '" + javaValueType.getName() + "'" );
// remove '"' from strings
if( valueType == ValueType.STRING && value.startsWith( "\"" ) && value.endsWith( "\"" ) )
value = value.substring( 1, value.length() - 1 );
} else {
// false, true
switch( value ) {
case "false": resultValueType[0] = ValueType.BOOLEAN; return false;
case "true": resultValueType[0] = ValueType.BOOLEAN; return true;
}
// check for function "lazy"
// Syntax: lazy(uiKey)
if( value.startsWith( "lazy(" ) && value.endsWith( ")" ) ) {
resultValueType[0] = ValueType.LAZY;
String uiKey = value.substring( 5, value.length() - 1 ).trim();
return (LazyValue) t -> {
return lazyUIManagerGet( uiKey );
};
}
// check whether value type is specified in the value
if( value.startsWith( "#" ) )
valueType = ValueType.COLOR;
else if( value.startsWith( "\"" ) && value.endsWith( "\"" ) ) {
valueType = ValueType.STRING;
value = value.substring( 1, value.length() - 1 );
} else if( value.startsWith( TYPE_PREFIX ) ) {
int end = value.indexOf( TYPE_PREFIX_END );
if( end != -1 ) {
try {
String typeStr = value.substring( TYPE_PREFIX.length(), end );
valueType = ValueType.valueOf( typeStr.toUpperCase( Locale.ENGLISH ) );
// remove type from value
value = value.substring( end + TYPE_PREFIX_END.length() );
} catch( IllegalArgumentException ex ) {
// ignore
}
}
}
}
// determine value type from key
if( valueType == ValueType.UNKNOWN ) {
if( key.endsWith( "UI" ) )
valueType = ValueType.STRING;
else if( key.endsWith( "Color" ) ||
(key.endsWith( "ground" ) &&
(key.endsWith( ".background" ) || key.endsWith( "Background" ) || key.equals( "background" ) ||
key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ) || key.equals( "foreground" ))) )
valueType = ValueType.COLOR;
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
valueType = ValueType.BORDER;
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) )
valueType = ValueType.ICON;
else if( key.endsWith( ".margin" ) || key.equals( "margin" ) ||
key.endsWith( ".padding" ) || key.equals( "padding" ) ||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
valueType = ValueType.INSETS;
else if( key.endsWith( "Size" ) )
valueType = ValueType.DIMENSION;
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
valueType = ValueType.INTEGER;
else if( key.endsWith( "Char" ) )
valueType = ValueType.CHARACTER;
else if( key.endsWith( "grayFilter" ) )
valueType = ValueType.GRAYFILTER;
// determine value type from key
if( valueType == ValueType.UNKNOWN ) {
if( key.endsWith( "UI" ) )
valueType = ValueType.STRING;
else if( key.endsWith( "Color" ) ||
(key.endsWith( "ground" ) &&
(key.endsWith( ".background" ) || key.endsWith( "Background" ) || key.equals( "background" ) ||
key.endsWith( ".foreground" ) || key.endsWith( "Foreground" ) || key.equals( "foreground" ))) )
valueType = ValueType.COLOR;
else if( key.endsWith( ".border" ) || key.endsWith( "Border" ) )
valueType = ValueType.BORDER;
else if( key.endsWith( ".icon" ) || key.endsWith( "Icon" ) )
valueType = ValueType.ICON;
else if( key.endsWith( ".margin" ) || key.equals( "margin" ) ||
key.endsWith( ".padding" ) || key.equals( "padding" ) ||
key.endsWith( "Margins" ) || key.endsWith( "Insets" ) )
valueType = ValueType.INSETS;
else if( key.endsWith( "Size" ) )
valueType = ValueType.DIMENSION;
else if( key.endsWith( "Width" ) || key.endsWith( "Height" ) )
valueType = ValueType.INTEGER;
else if( key.endsWith( "Char" ) )
valueType = ValueType.CHARACTER;
else if( key.endsWith( "grayFilter" ) )
valueType = ValueType.GRAYFILTER;
}
}
resultValueType[0] = valueType;
@@ -377,6 +415,7 @@ class UIDefaultsLoader
// parse value
switch( valueType ) {
case STRING: return value;
case BOOLEAN: return parseBoolean( value );
case CHARACTER: return parseCharacter( value );
case INTEGER: return parseInteger( value, true );
case FLOAT: return parseFloat( value, true );
@@ -869,6 +908,14 @@ class UIDefaultsLoader
return val;
}
private static Boolean parseBoolean( String value ) {
switch( value ) {
case "false": return false;
case "true": return true;
}
throw new IllegalArgumentException( "invalid boolean '" + value + "'" );
}
private static Character parseCharacter( String value ) {
if( value.length() != 1 )
throw new IllegalArgumentException( "invalid character '" + value + "'" );

View File

@@ -198,7 +198,7 @@ public class FlatStyleSupport
if( key.startsWith( "[" ) )
key = key.substring( key.indexOf( ']' ) + 1 );
return FlatLaf.parseDefaultsValue( key, value );
return FlatLaf.parseDefaultsValue( key, value, null );
}
/**

View File

@@ -0,0 +1,74 @@
/*
* 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;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import org.junit.jupiter.api.Test;
/**
* @author Karl Tauber
*/
public class TestUIDefaultsLoader
{
@Test
void parseValue() {
assertEquals( null, UIDefaultsLoader.parseValue( "dummy", "null", null ) );
assertEquals( false, UIDefaultsLoader.parseValue( "dummy", "false", null ) );
assertEquals( true, UIDefaultsLoader.parseValue( "dummy", "true", null ) );
assertEquals( "hello", UIDefaultsLoader.parseValue( "dummy", "hello", null ) );
assertEquals( "hello", UIDefaultsLoader.parseValue( "dummy", "\"hello\"", null ) );
assertEquals( "null", UIDefaultsLoader.parseValue( "dummy", "\"null\"", null ) );
assertEquals( 'a', UIDefaultsLoader.parseValue( "dummyChar", "a", null ) );
assertEquals( 123, UIDefaultsLoader.parseValue( "dummy", "123", null ) );
assertEquals( 123, UIDefaultsLoader.parseValue( "dummyWidth", "123", null ) );
assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummy", "1.23", null ) );
assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummyWidth", "{float}1.23", null ) );
assertEquals( new Insets( 2,2,2,2 ), UIDefaultsLoader.parseValue( "dummyInsets", "2,2,2,2", null ) );
assertEquals( new Dimension( 2,2 ), UIDefaultsLoader.parseValue( "dummySize", "2,2", null ) );
assertEquals( new Color( 0xff0000 ), UIDefaultsLoader.parseValue( "dummy", "#f00", null ) );
assertEquals( new Color( 0xff0000 ), UIDefaultsLoader.parseValue( "dummyColor", "#f00", null ) );
}
@Test
void parseValueWithJavaType() {
assertEquals( null, UIDefaultsLoader.parseValue( "dummy", "null", String.class ) );
assertEquals( false, UIDefaultsLoader.parseValue( "dummy", "false", boolean.class ) );
assertEquals( true, UIDefaultsLoader.parseValue( "dummy", "true", Boolean.class ) );
assertEquals( "hello", UIDefaultsLoader.parseValue( "dummy", "hello", String.class ) );
assertEquals( "hello", UIDefaultsLoader.parseValue( "dummy", "\"hello\"", String.class ) );
assertEquals( "null", UIDefaultsLoader.parseValue( "dummy", "\"null\"", String.class ) );
assertEquals( null, UIDefaultsLoader.parseValue( "dummy", "null", String.class ) );
assertEquals( 'a', UIDefaultsLoader.parseValue( "dummy", "a", char.class ) );
assertEquals( 'a', UIDefaultsLoader.parseValue( "dummy", "a", Character.class ) );
assertEquals( 123, UIDefaultsLoader.parseValue( "dummy", "123", int.class ) );
assertEquals( 123, UIDefaultsLoader.parseValue( "dummy", "123", Integer.class ) );
assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummy", "1.23", float.class ) );
assertEquals( 1.23f, UIDefaultsLoader.parseValue( "dummy", "1.23", Float.class ) );
assertEquals( new Insets( 2,2,2,2 ), UIDefaultsLoader.parseValue( "dummy", "2,2,2,2", Insets.class ) );
assertEquals( new Dimension( 2,2 ), UIDefaultsLoader.parseValue( "dummy", "2,2", Dimension.class ) );
assertEquals( new Color( 0xff0000 ), UIDefaultsLoader.parseValue( "dummy", "#f00", Color.class ) );
}
}