#1934: Better in memory config representation

This commit is contained in:
md_5 2016-08-15 18:41:52 +10:00
parent d6772cf1e4
commit 37b3cb4a30
3 changed files with 74 additions and 13 deletions

View File

@ -7,10 +7,7 @@ import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public final class Configuration public final class Configuration
{ {
@ -28,6 +25,25 @@ public final class Configuration
this( new LinkedHashMap<String, Object>(), defaults ); this( new LinkedHashMap<String, Object>(), defaults );
} }
Configuration(Map<?, ?> map, Configuration defaults)
{
this.self = new LinkedHashMap<>();
this.defaults = defaults;
for ( Map.Entry<?, ?> entry : map.entrySet() )
{
String key = entry.getKey().toString();
if ( entry.getValue() instanceof Map )
{
this.self.put( key, new Configuration( (Map) entry.getValue(), ( defaults == null ) ? null : defaults.getSection( key ) ) );
} else
{
this.self.put( key, entry.getValue() );
}
}
}
private Configuration getSectionFor(String path) private Configuration getSectionFor(String path)
{ {
int index = path.indexOf( SEPARATOR ); int index = path.indexOf( SEPARATOR );
@ -40,15 +56,11 @@ public final class Configuration
Object section = self.get( root ); Object section = self.get( root );
if ( section == null ) if ( section == null )
{ {
section = new LinkedHashMap<>(); section = new Configuration( ( defaults == null ) ? null : defaults.getSection( path ) );
self.put( root, section ); self.put( root, section );
} }
if ( section instanceof Configuration )
{
return (Configuration) section;
}
return new Configuration( (Map) section, ( defaults == null ) ? null : defaults.getSectionFor( path ) ); return (Configuration) section;
} }
private String getChild(String path) private String getChild(String path)
@ -71,6 +83,11 @@ public final class Configuration
val = section.get( getChild( path ), def ); val = section.get( getChild( path ), def );
} }
if ( val == null && def instanceof Configuration )
{
self.put( path, def );
}
return ( val != null ) ? (T) val : def; return ( val != null ) ? (T) val : def;
} }
@ -106,7 +123,7 @@ public final class Configuration
public Configuration getSection(String path) public Configuration getSection(String path)
{ {
Object def = getDefault( path ); Object def = getDefault( path );
return new Configuration( (Map) ( get( path, ( def instanceof Map ) ? def : Collections.EMPTY_MAP ) ), ( defaults == null ) ? null : defaults.getSection( path ) ); return (Configuration) get( path, ( def instanceof Configuration ) ? def : new Configuration( ( defaults == null ) ? null : defaults.getSection( path ) ) );
} }
/** /**

View File

@ -13,6 +13,10 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.representer.Represent;
import org.yaml.snakeyaml.representer.Representer;
@NoArgsConstructor(access = AccessLevel.PACKAGE) @NoArgsConstructor(access = AccessLevel.PACKAGE)
public class YamlConfiguration extends ConfigurationProvider public class YamlConfiguration extends ConfigurationProvider
@ -23,9 +27,24 @@ public class YamlConfiguration extends ConfigurationProvider
@Override @Override
protected Yaml initialValue() protected Yaml initialValue()
{ {
Representer representer = new Representer()
{
{
representers.put( Configuration.class, new Represent()
{
@Override
public Node representData(Object data)
{
return represent( ( (Configuration) data ).self );
}
} );
}
};
DumperOptions options = new DumperOptions(); DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK ); options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK );
return new Yaml( options );
return new Yaml( new Constructor(), representer, options );
} }
}; };

View File

@ -10,7 +10,7 @@ import org.junit.Test;
public class YamlConfigurationTest public class YamlConfigurationTest
{ {
private String document = "" private static final String TEST_DOCUMENT = ""
+ "receipt: Oz-Ware Purchase Invoice\n" + "receipt: Oz-Ware Purchase Invoice\n"
+ "date: 2012-08-06\n" + "date: 2012-08-06\n"
+ "customer:\n" + "customer:\n"
@ -43,11 +43,17 @@ public class YamlConfigurationTest
+ " Road to the Emerald City.\n" + " Road to the Emerald City.\n"
+ " Pay no attention to the\n" + " Pay no attention to the\n"
+ " man behind the curtain."; + " man behind the curtain.";
private static final String NUMBER_TEST = ""
+ "someKey:\n"
+ " 1: 1\n"
+ " 2: 2\n"
+ " 3: 3\n"
+ " 4: 4";
@Test @Test
public void testConfig() throws Exception public void testConfig() throws Exception
{ {
Configuration conf = ConfigurationProvider.getProvider( YamlConfiguration.class ).load( document ); Configuration conf = ConfigurationProvider.getProvider( YamlConfiguration.class ).load( TEST_DOCUMENT );
testSection( conf ); testSection( conf );
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
@ -77,5 +83,24 @@ public class YamlConfigurationTest
conf.set( "receipt", null ); conf.set( "receipt", null );
Assert.assertEquals( null, conf.get( "receipt" ) ); Assert.assertEquals( null, conf.get( "receipt" ) );
Assert.assertEquals( "foo", conf.get( "receipt", "foo" ) ); Assert.assertEquals( "foo", conf.get( "receipt", "foo" ) );
Configuration newSection = conf.getSection( "new.section" );
newSection.set( "value", "foo" );
Assert.assertEquals( "foo", conf.get( "new.section.value" ) );
conf.set( "other.new.section", "bar" );
Assert.assertEquals( "bar", conf.get( "other.new.section" ) );
}
@Test
public void testNumberedKeys()
{
Configuration conf = ConfigurationProvider.getProvider( YamlConfiguration.class ).load( NUMBER_TEST );
Configuration section = conf.getSection( "someKey" );
for ( String key : section.getKeys() )
{
// empty
}
} }
} }