Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
ValueProvider observers and PID fixes (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
rcahoon authored Mar 11, 2024
1 parent 7f3126a commit 28a84c7
Show file tree
Hide file tree
Showing 27 changed files with 847 additions and 415 deletions.
153 changes: 78 additions & 75 deletions src/main/java/com/team766/config/AbstractConfigValue.java
Original file line number Diff line number Diff line change
@@ -1,95 +1,98 @@
package com.team766.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import com.team766.library.AbstractObservable;
import com.team766.library.SettableValueProvider;
import com.team766.logging.Category;
import com.team766.logging.Logger;
import com.team766.logging.LoggerExceptionUtils;
import com.team766.logging.Severity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;

public abstract class AbstractConfigValue<E> implements SettableValueProvider<E> {
protected String m_key;
private E m_cachedValue;
private boolean m_cachedHasValue;
private int m_cachedGeneration = -1;
public abstract class AbstractConfigValue<E> extends AbstractObservable<Optional<E>>
implements SettableValueProvider<E> {
protected String m_key;
private E m_cachedValue;
private boolean m_cachedHasValue;

private static ArrayList<AbstractConfigValue<?>> c_accessedValues = new ArrayList<AbstractConfigValue<?>>();
private static ArrayList<AbstractConfigValue<?>> c_accessedValues =
new ArrayList<AbstractConfigValue<?>>();

static Collection<AbstractConfigValue<?>> accessedValues() {
return Collections.unmodifiableCollection(c_accessedValues);
}
static Collection<AbstractConfigValue<?>> accessedValues() {
return Collections.unmodifiableCollection(c_accessedValues);
}

static void resetStatics() {
c_accessedValues.clear();
}
static void resetStatics() {
c_accessedValues.clear();
}

protected AbstractConfigValue(final String key) {
m_key = key;
c_accessedValues.add(this);
// Querying for this config setting's key will add a placeholder entry
// in the config file if this setting does not already exist there.
ConfigFileReader.instance.getRawValue(m_key);
}
protected AbstractConfigValue(final String key) {
m_key = key;
c_accessedValues.add(this);
// Querying for this config setting's key will add a placeholder entry
// in the config file if this setting does not already exist there.
ConfigFileReader.instance.getRawValue(m_key);
update();
}

private void sync() {
if (ConfigFileReader.instance.getGeneration() != m_cachedGeneration) {
m_cachedGeneration = ConfigFileReader.instance.getGeneration();
var rawValue = ConfigFileReader.instance.getRawValue(m_key);
m_cachedHasValue = rawValue != null;
if (m_cachedHasValue) {
try {
m_cachedValue = parseJsonValue(rawValue);
} catch (Exception ex) {
Logger.get(Category.CONFIGURATION).logRaw(Severity.ERROR,
"Failed to parse " + m_key + " from the config file: "
+ LoggerExceptionUtils.exceptionToString(ex));
m_cachedValue = null;
m_cachedHasValue = false;
}
}
}
}
void update() {
var rawValue = ConfigFileReader.instance.getRawValue(m_key);
m_cachedHasValue = rawValue != null;
if (m_cachedHasValue) {
try {
m_cachedValue = parseJsonValue(rawValue);
} catch (Exception ex) {
Logger.get(Category.CONFIGURATION)
.logRaw(
Severity.ERROR,
"Failed to parse "
+ m_key
+ " from the config file: "
+ LoggerExceptionUtils.exceptionToString(ex));
m_cachedValue = null;
m_cachedHasValue = false;
}
}
notifyObservers(m_cachedHasValue ? Optional.of(m_cachedValue) : Optional.empty());
}

public String getKey() {
return m_key;
}
public String getKey() {
return m_key;
}

@Override
public boolean hasValue() {
sync();
return m_cachedHasValue;
}
@Override
public boolean hasValue() {
return m_cachedHasValue;
}

@Override
public E get() {
sync();
if (!m_cachedHasValue) {
throw new IllegalArgumentException(m_key + " not found in the config file");
}
return m_cachedValue;
}
@Override
public E get() {
if (!m_cachedHasValue) {
throw new IllegalArgumentException(m_key + " not found in the config file");
}
return m_cachedValue;
}

public void set(final E value) {
ConfigFileReader.instance.setValue(m_key, value);
}
public void set(final E value) {
ConfigFileReader.instance.setValue(m_key, value);
}

public void clear() {
ConfigFileReader.instance.setValue(m_key, null);
}
public void clear() {
ConfigFileReader.instance.setValue(m_key, null);
}

protected abstract E parseJsonValue(Object configValue);
protected abstract E parseJsonValue(Object configValue);

@Override
public String toString() {
sync();
if (!m_cachedHasValue) {
return "<unset>";
}
if (m_cachedValue == null) {
return "<null>";
}
return m_cachedValue.toString();
}
}
@Override
public String toString() {
if (!m_cachedHasValue) {
return "<unset>";
}
if (m_cachedValue == null) {
return "<null>";
}
return m_cachedValue.toString();
}
}
30 changes: 20 additions & 10 deletions src/main/java/com/team766/config/ConfigFileReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.regex.Pattern;
import org.json.JSONObject;
import org.json.JSONTokener;
Expand All @@ -29,10 +30,6 @@ public class ConfigFileReader {

private static final String KEY_DELIMITER = ".";

// This is incremented each time the config file is reloaded to ensure that ConfigValues use the
// most recent setting.
private int m_generation = 0;

private String m_fileName;
private String m_backupFileName; // if set, will also save here
private JSONObject m_values = new JSONObject();
Expand Down Expand Up @@ -81,12 +78,11 @@ public void reloadFromJson(final String jsonString) {
"Could not parse config value for " + param.getKey(), ex);
}
}
// All values parsed successfully; now actually apply the new values.
m_values = newValues;
++m_generation;
}

public int getGeneration() {
return m_generation;
for (AbstractConfigValue<?> param : AbstractConfigValue.accessedValues()) {
param.update();
}
}

public boolean containsKey(final String key) {
Expand Down Expand Up @@ -126,6 +122,13 @@ public <E> void setValue(final String key, final E value) {
String[] keyParts = splitKey(key);
JSONObject parentObj = getParent(m_values, keyParts);
parentObj.putOpt(keyParts[keyParts.length - 1], value == null ? JSONObject.NULL : value);

for (AbstractConfigValue<?> otherValue : AbstractConfigValue.accessedValues()) {
String[] otherValueKeyParts = splitKey(otherValue.getKey());
if (isPrefix(keyParts, otherValueKeyParts) || isPrefix(otherValueKeyParts, keyParts)) {
otherValue.update();
}
}
}

Object getRawValue(final String key) {
Expand All @@ -152,10 +155,17 @@ private static Object getRawValue(final JSONObject obj, final String key) {
return rawValue;
}

private static String[] splitKey(final String key) {
static String[] splitKey(final String key) {
return key.split(Pattern.quote(KEY_DELIMITER));
}

static boolean isPrefix(final String[] a, final String[] b) {
if (a.length > b.length) {
return false;
}
return Arrays.equals(a, 0, a.length, b, 0, a.length);
}

private static JSONObject getParent(JSONObject obj, final String[] keyParts) {
for (int i = 0; i < keyParts.length - 1; ++i) {
JSONObject subObj;
Expand Down
Loading

0 comments on commit 28a84c7

Please sign in to comment.