All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.l4.commons.config.internal.DefaultConfig Maven / Gradle / Ivy

The newest version!
package se.l4.commons.config.internal;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.validation.ConstraintViolation;
import javax.validation.Path;
import javax.validation.Path.Node;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import com.google.common.base.Joiner;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;

import se.l4.commons.config.Config;
import se.l4.commons.config.ConfigException;
import se.l4.commons.config.ConfigKey;
import se.l4.commons.config.Value;
import se.l4.commons.config.internal.streaming.MapInput;
import se.l4.commons.serialization.Serializer;
import se.l4.commons.serialization.SerializerCollection;
import se.l4.commons.serialization.WrappedSerializerCollection;
import se.l4.commons.serialization.format.StreamingInput;

/**
 * Default implementation of {@link Config}.
 * 
 * @author Andreas Holstenson
 *
 */
public class DefaultConfig
	implements Config
{
	private final SerializerCollection collection;
	private final Map data;
	private final ValidatorFactory validatorFactory;
	
	public DefaultConfig(SerializerCollection collection, 
			ValidatorFactory validatorFactory, 
			File root, 
			Map data)
	{
		this.collection = new WrappedSerializerCollection(collection);
		this.validatorFactory = validatorFactory;
		this.data = data;
		
		collection.bind(File.class, new FileSerializer(root));
		collection.bind(ConfigKey.class, new ConfigKey.ConfigKeySerializer(this));
	}
	
	private static final Pattern LIST_GET = Pattern.compile("(.+)(?:\\[([0-9]+)\\])+");
	
	private String get(String path, int start, int end)
	{
		if(path.charAt(start) == '"') start += 1;
		if(path.charAt(end - 1) == '"') end -= 1;
		
		return path.substring(start, end);
	}
	
	private Object get(String path)
	{
		if(path == null || path.equals(""))
		{
			return data;
		}
		
		List parts = Lists.newArrayList();
		boolean quoted = false;
		int lastIndex = 0;
		for(int i=0, n=path.length(); i current = data;
		for(int i=0, n=parts.size(); i T asObject(String path, Class type)
	{
		return get(path, type).getOrDefault(null);
	}
	
	@Override
	public Collection keys(String path)
	{
		if(path == null || path.equals(""))
		{
			return data.keySet();
		}
			
		String[] parts = path.split("\\.");
		Map current = data;
		for(int i=0, n=parts.length; i ! s.startsWith("_aurochs_:"));
	}
	
	private void validateInstance(String path, Object object)
	{
		if(validatorFactory == null) return;
		
		Validator validator = validatorFactory.getValidator();
		Set> violations = validator.validate(object);
		
		if(violations.isEmpty())
		{
			// No violations
			return;
		}
		
		StringBuilder builder = new StringBuilder("Validation failed for `" + path + "`:\n");
		
		for(ConstraintViolation violation : violations)
		{
			builder
				.append("* ")
				.append(join(violation.getPropertyPath()))
				.append(violation.getMessage())
				.append("\n");
		}
		
		throw new ConfigException(builder.toString());
	}
	
	private String join(Path path)
	{
		StringBuilder builder = new StringBuilder();
		for(Node node : path)
		{
			if(builder.length() > 0)
			{
				builder.append(".");
			}
			
			builder.append(node.getName());
		}
		
		if(builder.length() > 0)
		{
			builder.append(": ");
		}
		
		return builder.toString();
	}
	
	@Override
	public  Value get(String path, Class type)
	{
		Serializer serializer = collection.find(type);
		if(serializer == null)
		{
			throw new ConfigException("Unable to find a serializer suitable for " + type);
		}
		
		Object data = get(path);
		if(data == null)
		{
			return new ValueImpl(path, false, null);
		}
		
		StreamingInput input = MapInput.resolveInput(path, data);
		try
		{
			T instance = serializer.read(input);
			
			validateInstance(path, instance);
			
			return new ValueImpl(path, true, instance);
		}
		catch(ConfigException e)
		{
			throw e;
		}
		catch(Exception e)
		{
			throw new ConfigException("Unable to create " + type + " from data at `" + path + "`; " + e.getMessage(), e);
		}
	}
	
	private static class ValueImpl
		implements Value
	{
		private final String path;
		private final boolean exists;
		private final T instance;

		public ValueImpl(String path, boolean exists, T instance)
		{
			this.path = path;
			this.exists = exists;
			this.instance = instance;
		}

		@Override
		public T get()
		{
			if(! exists)
			{
				throw new ConfigException("Unable to get config value at `" + path + "`");
			}
			return instance;
		}
		
		@Override
		public T getOrDefault(T defaultInstance)
		{
			return exists ? instance : defaultInstance;
		}
		
		@Override
		public boolean exists()
		{
			return exists;
		}
	}
}