/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.storage.settings;
import com.jcwhatever.nucleus.mixins.IDisposable;
import com.jcwhatever.nucleus.storage.IDataNode;
import com.jcwhatever.nucleus.utils.EnumUtils;
import com.jcwhatever.nucleus.utils.PreCon;
import com.jcwhatever.nucleus.utils.converters.Converter;
import com.jcwhatever.nucleus.utils.coords.LocationUtils;
import com.jcwhatever.nucleus.utils.items.ItemStackUtils;
import com.jcwhatever.nucleus.managed.items.serializer.InvalidItemStackStringException;
import com.jcwhatever.nucleus.utils.observer.update.IUpdateSubscriber;
import com.jcwhatever.nucleus.utils.observer.update.NamedUpdateAgents;
import com.jcwhatever.nucleus.utils.text.TextUtils;
import com.jcwhatever.nucleus.utils.validate.IValidator;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
/**
* An implementation of {@link ISettingsManager}.
*/
public class SettingsManager implements ISettingsManager, IDisposable {
private final IDataNode _dataNode;
private final NamedUpdateAgents _agents = new NamedUpdateAgents();
private Map<String, PropertyDefinition> _definitions;
private boolean _isDisposed;
/**
* Constructor.
*
* <p>Initializes manager without property definitions.</p>
*
* @param dataNode The data node to manage.
*/
public SettingsManager(IDataNode dataNode) {
this(dataNode, null);
}
/**
* Constructor.
*
* @param dataNode The data node to manage.
* @param definitions Property definitions.
*/
public SettingsManager(IDataNode dataNode, @Nullable Map<String, PropertyDefinition> definitions) {
_dataNode = dataNode;
_definitions = Collections.unmodifiableMap(
definitions == null ? new HashMap<String, PropertyDefinition>(0) : definitions);
}
@Override
public Map<String, PropertyDefinition> getDefinitions() {
return _definitions;
}
@Override
public boolean isProperty(String propertyName) {
PreCon.notNull(propertyName);
return _definitions.containsKey(propertyName);
}
@Override
public boolean set(String propertyName, @Nullable Object value) {
PreCon.notNull(propertyName);
PropertyDefinition definition = _definitions.get(propertyName);
if (definition == null)
return false;
Converter<?> converter = definition.getConverter();
if (converter != null) {
value = converter.convert(value);
}
IValidator<Object> validator = definition.getValidator();
if (!(validator != null && !validator.isValid(value)) && _dataNode.set(propertyName, value)) {
if (_agents.hasAgent("onChange"))
_agents.update("onChange", new PropertyValue(this, definition, value));
return true;
}
return false;
}
@Nullable
@Override
public <T> T get(String propertyName) {
PreCon.notNull(propertyName);
PropertyDefinition definition = _definitions.get(propertyName);
if (definition == null)
return null;
@SuppressWarnings("unchecked")
T result = (T)_dataNode.get(propertyName);
if (result == null) {
@SuppressWarnings("unchecked")
T defaultValue = (T) definition.getDefaultValue();
return defaultValue;
}
else {
switch (definition.getValueType().getType()) {
case BOOLEAN:
if (result instanceof String) {
@SuppressWarnings("unchecked")
T bool = (T)(Boolean) TextUtils.parseBoolean((String) result);
return bool;
}
break;
case INTEGER:
if (result instanceof String) {
@SuppressWarnings("unchecked")
T integer = (T)(Integer)TextUtils.parseInt((String)result, 0);
return integer;
}
else if (result instanceof Long) {
@SuppressWarnings("unchecked")
T integerByte = (T)(Integer)((Long)result).intValue();
return integerByte;
}
break;
case LONG:
if (result instanceof String) {
@SuppressWarnings("unchecked")
T l = (T)(Long)TextUtils.parseLong((String)result, 0L);
return l;
}
else if (result instanceof Integer) {
@SuppressWarnings("unchecked")
T longInteger = (T)(Long)((Integer)result).longValue();
return longInteger;
}
break;
case ITEM_STACK_ARRAY:
if (result instanceof String) {
try {
@SuppressWarnings("unchecked")
T itemStacks = (T) ItemStackUtils.parse((String) result);
return itemStacks;
} catch (InvalidItemStackStringException e) {
e.printStackTrace();
return null;
}
}
break;
case LOCATION:
if (result instanceof String) {
@SuppressWarnings("unchecked")
T location = (T) LocationUtils.parseLocation((String) result);
return location;
}
break;
case UNIQUE_ID:
if (result instanceof String) {
@SuppressWarnings("unchecked")
T uuid = (T) TextUtils.parseUUID((String) result);
return uuid;
}
break;
case ENUM:
if (result instanceof String) {
@SuppressWarnings("unchecked")
T enumeration = (T) EnumUtils.searchEnum(
(String) result, (Class<Enum>) definition.getValueType().getTypeClass());
return enumeration;
}
break;
}
}
return result;
}
@Nullable
@Override
public <T> T getUnconverted(String propertyName) {
PreCon.notNull(propertyName);
PropertyDefinition definition = _definitions.get(propertyName);
if (definition == null)
return null;
Object result = get(propertyName);
if (result == null)
return null;
if (definition.getUnconverter() != null) {
@SuppressWarnings("unchecked")
T unconverted = (T)definition.getUnconverter().convert(result);
return unconverted;
}
@SuppressWarnings("unchecked")
T casted = (T)result;
return casted;
}
@Override
public boolean getBoolean(String propertyName) {
PreCon.notNull(propertyName);
Boolean result = getDefaultValue(propertyName);
return _dataNode.getBoolean(propertyName, result != null ? result : false);
}
@Override
public int getInteger(String propertyName) {
PreCon.notNull(propertyName);
Integer i = getDefaultValue(propertyName);
return _dataNode.getInteger(propertyName, i != null ? i : 0);
}
@Override
public long getLong(String propertyName) {
PreCon.notNull(propertyName);
Long l = getDefaultValue(propertyName);
return _dataNode.getLong(propertyName, l != null ? l : 0);
}
@Override
public double getDouble(String propertyName) {
PreCon.notNull(propertyName);
Double d = getDefaultValue(propertyName);
return _dataNode.getDouble(propertyName, d != null ? d : 0);
}
@Nullable
@Override
public String getString(String propertyName) {
PreCon.notNull(propertyName);
return _dataNode.getString(propertyName, (String) getDefaultValue(propertyName));
}
@Nullable
@Override
public Location getLocation(String propertyName) {
PreCon.notNull(propertyName);
return _dataNode.getLocation(propertyName, (Location) getDefaultValue(propertyName));
}
@Nullable
@Override
public ItemStack[] getItemStacks(String propertyName) {
PreCon.notNull(propertyName);
return _dataNode.getItemStacks(propertyName, (ItemStack[]) getDefaultValue(propertyName));
}
@Nullable
@Override
public UUID getUUID(String propertyName) {
PreCon.notNull(propertyName);
return _dataNode.getUUID(propertyName, (UUID) getDefaultValue(propertyName));
}
@Nullable
@Override
public <T extends Enum<T>> T getEnum(String propertyName, Class<T> type) {
PreCon.notNull(propertyName);
PreCon.notNull(type);
@SuppressWarnings("unchecked")
T result = _dataNode.getEnum(propertyName, (T)getDefaultValue(propertyName), type);
return result;
}
@Override
public boolean isDisposed() {
return _isDisposed;
}
@Override
public void dispose() {
_isDisposed = true;
_agents.disposeAgents();
}
/**
* Add a subscriber that is updated whenever a setting is changed.
*
* @param subscriber The subscriber to add.
*/
public void onChange(IUpdateSubscriber<PropertyValue> subscriber) {
PreCon.notNull(subscriber);
_agents.getAgent("onChange").addSubscriber(subscriber);
}
@Nullable
private <T> T getDefaultValue(String propertyName) {
PropertyDefinition definition = _definitions.get(propertyName);
if (definition == null)
return null;
@SuppressWarnings("unchecked")
T result = (T)definition.getDefaultValue();
return result;
}
/**
* Contains a property and a value for the property.
*/
public static class PropertyValue {
public final SettingsManager manager;
public final PropertyDefinition definition;
public final Object value;
PropertyValue(SettingsManager manager, PropertyDefinition definition, @Nullable Object value) {
this.manager = manager;
this.definition = definition;
this.value = value;
}
}
}