/*
*
* Goko
* Copyright (C) 2013 PsyKo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.goko.controller.tinyg.commons.configuration;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.goko.core.common.exception.GkException;
import org.goko.core.common.exception.GkFunctionalException;
import org.goko.core.common.exception.GkTechnicalException;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;
/**
* Abstract implementation of TinyG based board configuration
*
* @author PsyKo
*
*/
public abstract class AbstractTinyGConfiguration<C extends AbstractTinyGConfiguration<C>> {
/** The list of handled groups */
private List<TinyGGroupSettings> groups;
/**
* Constructor
*/
public AbstractTinyGConfiguration(){
groups = new ArrayList<TinyGGroupSettings>();
}
/**
* Add the given group
* @param group the group to add
*/
public void addGroup(TinyGGroupSettings group){
groups.add(group);
}
/**
* @return the groups
*/
public List<TinyGGroupSettings> getGroups() {
return groups;
}
/**
* Returns the group for the given identifier
* @param identifier the identifier of the requested group
* @return TinyGGroupSettings
*/
public TinyGGroupSettings getGroup(String identifier) {
for (TinyGGroupSettings tinyGGroupSettings : groups) {
if(StringUtils.equals(tinyGGroupSettings.getGroupIdentifier(), identifier)){
return tinyGGroupSettings;
}
}
return null;
}
/**
* @param groups the groups to set
*/
public void setGroups(List<TinyGGroupSettings> groups) {
this.groups = groups;
}
/**
* Returns the setting as the specified type amongst the given list of settings
* @param identifier the identifier
* @param lstSettings th settings to go through
* @param clazz the expected type
* @return the value as clazz
* @throws GkException GkException
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private <T> T getSetting(String identifier, List<TinyGSetting<?>> lstSettings, Class<T> clazz) throws GkException{
for(TinyGSetting setting : lstSettings){
if(StringUtils.equalsIgnoreCase( setting.getIdentifier(), identifier ) ){
if(setting.getType() != clazz){
throw new GkTechnicalException("Cannot retrieve setting '"+identifier+"' type. Requesting "+clazz+"', got'"+setting.getType()+"'. ");
}
return (T) setting.getValue();
}
}
throw new GkFunctionalException("Setting '"+identifier+"' is unknown");
}
/**
* Returns the setting as a String
* @param groupIdentifier the identifier of the group
* @param identifier the identifier
* @return the value as a String
* @throws GkException GkException
*/
public String getSetting(String groupIdentifier, String identifier) throws GkException{
return getSetting(groupIdentifier, identifier, String.class);
}
/**
* Returns the setting as the specified type or null if not found
* @param groupIdentifier the identifier of the group
* @param identifier the identifier
* @param clazz the expected type
* @return the value as clazz
* @throws GkException GkException
*/
public <T> T findSetting(String groupIdentifier, String identifier, Class<T> clazz) throws GkException{
for(TinyGGroupSettings grpSetting : groups){
if(StringUtils.equalsIgnoreCase( grpSetting.getGroupIdentifier(), groupIdentifier ) ){
T setting = getSetting(identifier,grpSetting.getSettings(), clazz);
return setting;
}
}
return null;
}
/**
* Returns the setting as the specified type
* @param groupIdentifier the identifier of the group
* @param identifier the identifier
* @param clazz the expected type
* @return the value as clazz
* @throws GkException GkException
*/
public <T> T getSetting(String groupIdentifier, String identifier, Class<T> clazz) throws GkException{
for(TinyGGroupSettings grpSetting : groups){
if(StringUtils.equalsIgnoreCase( grpSetting.getGroupIdentifier(), groupIdentifier ) ){
T setting = getSetting(identifier,grpSetting.getSettings(), clazz);
if(setting == null){
throw new GkFunctionalException("Setting '"+identifier+"' is unknown for group "+groupIdentifier);
}
return setting;
}
}
throw new GkFunctionalException("Unknown group "+groupIdentifier);
}
/**
* Sets the setting value
* @param groupIdentifier the identifier of the group
* @param identifier the identifier
* @param value the value to set
* @throws GkException GkException
*/
public <T> void setSetting(String groupIdentifier, String identifier, T value) throws GkException{
for(TinyGGroupSettings grpSetting : groups){
if(StringUtils.equalsIgnoreCase( grpSetting.getGroupIdentifier(), groupIdentifier ) ){
setSetting(grpSetting, identifier, value);
return;
}
}
throw new GkFunctionalException("Setting '"+identifier+"' from group '"+groupIdentifier+"' is unknown");
}
/**
* Sets the setting value
* @param group the TinyGGroupSettings
* @param identifier the identifier
* @param value the value to set
* @throws GkException GkException
*/
@SuppressWarnings("unchecked")
private <T> void setSetting(TinyGGroupSettings group, String identifier, T value) throws GkException{
for(TinyGSetting<?> setting : group.getSettings()){
if(StringUtils.equalsIgnoreCase( setting.getIdentifier(), identifier ) ){
if(value != null && setting.getType() != value.getClass()){
throw new GkTechnicalException("Setting '"+identifier+"' type mismatch. Expecting "+setting.getType()+"', got'"+value.getClass()+"'. ");
}
((TinyGSetting<T>)setting).setValue(value);
return;
}
}
}
/**
* Determines if this configuration was completely assigned using at least once the setValue(..) method on every setting
* @return <code>true</code> if all settings were assigned, <code>false</code> otherwise
*/
public boolean isCompletelyLoaded(){
for (TinyGGroupSettings tinyGGroupSettings : groups) {
if(!tinyGGroupSettings.isCompletelyLoaded()){
return false;
}
}
return true;
}
public void copyFrom(final AbstractTinyGConfiguration<C> baseConfig){
for (TinyGGroupSettings tinyGGroupSettings : baseConfig.getGroups()) {
addGroup(tinyGGroupSettings.copy());
}
}
/**
* Creates a new instance of this configuration
* @return C
*/
protected abstract C newInstance();
/**
* Returns a copy of this configuration
* @return C
*/
public C getCopy(){
C newInstance = newInstance();
newInstance.copyFrom(this);
return newInstance;
}
/**
* Returns a configuration containing only the values that differ from the base configuration. Other values are null
* @param baseConfig the base configuration
* @param newConfig the new configuration
* @return a differential configuration
* @throws GkException GkException
*/
@SuppressWarnings("unchecked")
public C getDifferentialConfiguration(C otherConfig) throws GkException{
C diffConfig = newInstance();
diffConfig.copyFrom(this);
for(TinyGGroupSettings group : getGroups()){
List<TinyGSetting<?>> settings = group.getSettings();
for (TinyGSetting<?> tinyGSetting : settings) {
Object baseValue = tinyGSetting.getValue();
Object newValue = otherConfig.getSetting(group.getGroupIdentifier(), tinyGSetting.getIdentifier(), tinyGSetting.getType());
if(!ObjectUtils.equals(baseValue, newValue)){
diffConfig.setSetting(group.getGroupIdentifier(), tinyGSetting.getIdentifier(), newValue);
}else{
diffConfig.setSetting(group.getGroupIdentifier(), tinyGSetting.getIdentifier(), null);
}
}
}
return diffConfig;
}
/**
* Build the given configuration using the JSon object as input and support for recursive levels
* @param config the target configuration
* @param json the JSon object to get values from
* @throws GkException GkException
*/
public void setFromJson(JsonObject json) throws GkException{
setFromJson(json, null);
}
/**
* Build the given configuration using the JSon object as input and support for recursive levels
* @param config the target configuration
* @param json the JSon object to get values from
* @param identifierPrefix the current identifier prefix for JSon group handling
* @throws GkException GkException
*/
protected void setFromJson(JsonObject json, String identifierPrefix) throws GkException{
JsonObject jsonObj = json;
for(String name : jsonObj.names()){
JsonValue subObj = jsonObj.get(name);
if(subObj.isObject()){
setFromJson((JsonObject)subObj, name);
}else{
if(StringUtils.isBlank(identifierPrefix)){
setSetting(getDefaultGroup(), name, getValue(subObj));
}else{
setSetting(identifierPrefix, name, getValue(subObj));
}
}
}
}
/**
* Returns the default group (usually the system group)
* @return the default group
*/
protected abstract String getDefaultGroup();
/**
* Returns the value of the given JsonValue
* @param jsonValue the JsonValue to get value of
* @return Object
*/
private static Object getValue(JsonValue jsonValue){
if(jsonValue.isNumber()){
return jsonValue.asBigDecimal();
}else if(jsonValue.isString()){
return jsonValue.asString();
}
return null;
}
}