{
-
-
+
/**
* Indicates that a component has been added to the configuration. This
* method will only be called when a component of a type that the listener
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Config.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Config.java
index 2622dbe0..107faca2 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Config.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Config.java
@@ -38,7 +38,7 @@
* An annotation that can be added directly to variables that should be
* configurable. We don't require a default value, as we can assign that
* default during construction by declaring the annotated field with a value.
- *
+ *
* Config should not be applied to a static field.
*/
@Documented
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigManager.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigManager.java
index 88fdd714..2182de63 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigManager.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigManager.java
@@ -36,9 +36,9 @@
/**
* An annotation that denotes where the ConfigurationManager should be stored.
- *
+ *
* Use this sparingly.
- *
+ *
* ConfigManager should not be applied to a static field.
*/
@Documented
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurableName.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurableName.java
index f3b95289..53a5fba3 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurableName.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurableName.java
@@ -36,7 +36,7 @@
/**
* An annotation that denotes where the String value of the component's name should be stored.
- *
+ *
* ConfigurableName should not be applied to a static field.
*/
@Documented
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurationData.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurationData.java
index 2db6cd86..7a66bcd7 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurationData.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurationData.java
@@ -2,7 +2,7 @@
* Copyright 1999-2004 Carnegie Mellon University.
* Portions Copyright 2004 Sun Microsystems, Inc.
* Portions Copyright 2003 Mitsubishi Electric Research Laboratories.
- * Copyright (c) 2004-2021, Oracle and/or its affiliates.
+ * Copyright (c) 2004, 2025, Oracle and/or its affiliates.
*
* Licensed under the 2-clause BSD license.
*
@@ -39,7 +39,6 @@
import com.oracle.labs.mlrg.olcut.config.property.SimpleProperty;
import com.oracle.labs.mlrg.olcut.util.Util;
-import java.io.Serializable;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.format.DateTimeParseException;
@@ -60,45 +59,17 @@
/**
* Carrier for property data. Principally a {@link Map} from {@link String} to {@link Property}, and
* a class name.
+ *
+ * @param name The name of the configured object.
+ * @param className The class name of the configured object.
+ * @param properties The properties to apply to that object.
+ * @param serializedForm A URL for a resource indicating from where the component can be
+ * deserialized.
*/
-public final class ConfigurationData implements Serializable {
+public record ConfigurationData(String name, String className, Map properties,
+ String serializedForm) {
private static final Logger logger = Logger.getLogger(ConfigurationData.class.getName());
- private static final long serialVersionUID = 1L;
-
- public static final long DEFAULT_LEASE_TIME = -1;
-
- private final String name;
-
- private final String className;
-
- private final Map properties;
-
- /**
- * A URL for a resource indicating from where the component can be
- * deserialized.
- */
- private final String serializedForm;
-
- /**
- * Whether this component is exportable to a service registrar.
- */
- private final boolean exportable;
-
- private final boolean importable;
-
- /**
- * The time to lease this object.
- */
- private final long leaseTime;
-
- /**
- * The (possibly null) name of a component that has a list
- * of configuration entries to use when registering this component with
- * a service registrar.
- */
- private final String entriesName;
-
/**
* Creates an empty ConfigurationData.
* @param name the name of the item
@@ -116,7 +87,7 @@ public ConfigurationData(String name, String className) {
* @param properties The properties to apply to that object.
*/
public ConfigurationData(String name, String className, Map properties) {
- this(name,className,properties,null,null,false,false,DEFAULT_LEASE_TIME);
+ this(name,className,properties,null);
}
/**
@@ -124,36 +95,25 @@ public ConfigurationData(String name, String className, Map pr
* @param name The name of the configured object.
* @param className The class name of the configured object.
* @param serializedForm A path to load the serialised form of this object (or null).
- * @param entriesName The entries to restrict Jini loading (or null).
- * @param exportable Is this object exportable via a Jini registry.
- * @param importable Should this object be imported via a Jini registry.
- * @param leaseTime How long before the Jini registrar needs to have the object renewed.
*/
- public ConfigurationData(String name, String className, String serializedForm, String entriesName, boolean exportable, boolean importable, long leaseTime) {
- this(name,className, Collections.emptyMap(),serializedForm,entriesName,exportable,importable,leaseTime);
+ public ConfigurationData(String name, String className, String serializedForm) {
+ this(name,className,Collections.emptyMap(),serializedForm);
}
/**
* Creates a ConfigurationData with the specified properties. The properties are validated elsewhere as
* this does not trigger class loading.
- * @param name The name of the configured object.
- * @param className The class name of the configured object.
- * @param properties The properties to apply to that object.
+ *
+ * @param name The name of the configured object.
+ * @param className The class name of the configured object.
+ * @param properties The properties to apply to that object.
* @param serializedForm A path to load the serialised form of this object (or null).
- * @param entriesName The entries to restrict Jini loading (or null).
- * @param exportable Is this object exportable via a Jini registry.
- * @param importable Should this object be imported via a Jini registry.
- * @param leaseTime How long before the Jini registrar needs to have the object renewed.
*/
- public ConfigurationData(String name, String className, Map properties, String serializedForm, String entriesName, boolean exportable, boolean importable, long leaseTime) {
+ public ConfigurationData(String name, String className, Map properties, String serializedForm) {
this.name = name;
this.className = className;
this.properties = new HashMap<>(properties);
this.serializedForm = serializedForm;
- this.entriesName = entriesName;
- this.exportable = exportable;
- this.importable = importable;
- this.leaseTime = leaseTime;
}
/**
@@ -166,68 +126,18 @@ public void add(String propName, Property propValue) {
properties.put(propName, propValue);
}
- /** @return Returns the className. */
- public String getClassName() {
- return className;
- }
-
- /** @return Returns the name. */
- public String getName() {
- return name;
- }
-
- /**
- * Returns the path to the serialised form.
- * @return The path to the serialised form.
- */
- public String getSerializedForm() {
- return serializedForm;
- }
-
- /**
- * Should this configuration import a remote object via Jini.
- * @return Should the configuration import a remote object.
- */
- @Deprecated
- public boolean isImportable() {
- return importable;
- }
-
/**
- * Returns the Jini lease time. Defaults to -1, Leases.ANY.
- * @return The Jini lease time.
+ * @return Returns an unmodifiable view on the properties.
*/
- @Deprecated
- public long getLeaseTime() {
- return leaseTime;
- }
-
- /**
- * Should this configuration export it's object via Jini.
- * @return Should the configuration export a remote object.
- */
- @Deprecated
- public boolean isExportable() {
- return exportable;
- }
-
- /**
- * Returns the entries which control Jini lookup.
- * @return The Jini control entries.
- */
- @Deprecated
- public String getEntriesName() {
- return entriesName;
- }
-
- /** @return Returns an unmodifiable view on the properties. */
- public Map getProperties() {
+ @Override
+ public Map properties() {
return Collections.unmodifiableMap(properties);
}
/**
* Returns the value associated with that property name, or {@link Optional#empty}
* if it doesn't exist.
+ *
* @param propertyName The property name.
* @return The {@link Optional#of} the property value or optional empty.
*/
@@ -253,30 +163,11 @@ public boolean contains(String propName) {
/**
* Copies this ConfigurationData. The copy is disconnected from the original as
* it contains a different map (though all the elements are immutable and the same references).
+ *
* @return A copy of this object.
*/
public ConfigurationData copy() {
- return new ConfigurationData(name,className,properties,serializedForm,entriesName,exportable,importable,leaseTime);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof ConfigurationData)) return false;
- ConfigurationData that = (ConfigurationData) o;
- return exportable == that.exportable &&
- importable == that.importable &&
- leaseTime == that.leaseTime &&
- name.equals(that.name) &&
- className.equals(that.className) &&
- properties.equals(that.properties) &&
- Objects.equals(serializedForm, that.serializedForm) &&
- Objects.equals(entriesName, that.entriesName);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(name, className, properties, serializedForm, exportable, importable, leaseTime, entriesName);
+ return new ConfigurationData(name, className, properties, serializedForm);
}
@Override
@@ -286,10 +177,6 @@ public String toString() {
", className='" + className + '\'' +
", properties=" + properties +
", serializedForm='" + serializedForm + '\'' +
- ", exportable=" + exportable +
- ", importable=" + importable +
- ", leaseTime=" + leaseTime +
- ", entriesName='" + entriesName + '\'' +
')';
}
@@ -320,27 +207,27 @@ public StructuralConfigurationData(Map contextMap, Co
this.listProperties = new HashMap<>();
this.listClassProperties = new HashMap<>();
this.mapProperties = new HashMap<>();
- for(Map.Entry propertyEntry: referencedCD.properties.entrySet()) {
+ for (Map.Entry propertyEntry : referencedCD.properties.entrySet()) {
String propName = propertyEntry.getKey();
Property prop = propertyEntry.getValue();
- if(prop instanceof SimpleProperty) {
- this.simpleProperties.put(propName, new DerefedProperty(contextMap, (SimpleProperty) prop, propName, ""));
- } else if (prop instanceof ListProperty) {
- ListProperty listProperty = (ListProperty) prop;
- this.listProperties.put(propName,
- IntStream.range(0, listProperty.getSimpleList().size())
- .mapToObj(i ->
- new DerefedProperty(contextMap, listProperty.getSimpleList().get(i), propName, ", index: " + i))
- .collect(Collectors.toList()));
- this.listClassProperties.put(propName, listProperty.getClassList());
- } else if (prop instanceof MapProperty) {
- MapProperty mapProperty = (MapProperty) prop;
- this.mapProperties.put(propName, mapProperty.getMap().entrySet().stream()
- .collect(Collectors.toMap(Map.Entry::getKey,
- e -> new DerefedProperty(contextMap, e.getValue(),
- propName, ", PropValue key: " + e.getKey()))));
- } else {
- logger.fine(String.format("Unknown Property of key: %s with type: %s", propertyEntry.getKey(), prop.getClass()));
+ switch (prop) {
+ case SimpleProperty simpleProperty ->
+ this.simpleProperties.put(propName, new DerefedProperty(contextMap, simpleProperty, propName, ""));
+ case ListProperty listProperty -> {
+ this.listProperties.put(propName,
+ IntStream.range(0, listProperty.simpleList().size())
+ .mapToObj(i ->
+ new DerefedProperty(contextMap, listProperty.simpleList().get(i), propName, ", index: " + i))
+ .collect(Collectors.toList()));
+ this.listClassProperties.put(propName, listProperty.classList());
+ }
+ case MapProperty mapProperty ->
+ this.mapProperties.put(propName, mapProperty.map().entrySet().stream()
+ .collect(Collectors.toMap(Map.Entry::getKey,
+ e -> new DerefedProperty(contextMap, e.getValue(),
+ propName, ", PropValue key: " + e.getKey()))));
+ case null, default ->
+ logger.fine(String.format("Unknown Property of key: %s with type: %s", propertyEntry.getKey(), prop.getClass()));
}
}
}
@@ -374,14 +261,13 @@ public int hashCode() {
@Override
public boolean equals(Object o) {
- if (o instanceof StructuralConfigurationData) {
- StructuralConfigurationData that = (StructuralConfigurationData) o;
+ if (o instanceof StructuralConfigurationData that) {
if (this.className.equals(that.className)) {
boolean simpleMatch = checkPresenceAllMatch(this.simpleProperties, that.simpleProperties,
DerefedProperty::equals);
- if(!simpleMatch) {
- logger.fine("SimpleProperties don't match: as: " + this.simpleProperties + " bs: " +that.simpleProperties);
+ if (!simpleMatch) {
+ logger.fine("SimpleProperties don't match: as: " + this.simpleProperties + " bs: " + that.simpleProperties);
}
boolean mapMatch = checkPresenceAllMatch(this.mapProperties, that.mapProperties,
@@ -390,27 +276,27 @@ public boolean equals(Object o) {
aMap.keySet().stream()
.allMatch(k -> aMap.get(k).equals(bMap.get(k))));
- if(!mapMatch) {
+ if (!mapMatch) {
logger.fine("MapProperties don't match: as: " + this.mapProperties + " bs: " + that.mapProperties);
}
- boolean listMatch = checkPresenceAllMatch(this.listProperties,that.listProperties, (as, bs) -> {
- boolean eq = Util.bagEquality(as, bs);
- if(!eq) {
- logger.fine("ListProperties not equal using bag equality:\na: " + as.toString() + "\nb: " + bs.toString());
+ boolean listMatch = checkPresenceAllMatch(this.listProperties, that.listProperties, (as, bs) -> {
+ boolean eq = Util.bagEquality(as, bs);
+ if (!eq) {
+ logger.fine("ListProperties not equal using bag equality:\na: " + as.toString() + "\nb: " + bs.toString());
}
return eq;
}) &&
checkPresenceAllMatch(this.listClassProperties, that.listClassProperties, (as, bs) -> {
boolean eq = Util.bagEquality(as, bs);
- if(!eq) {
- logger.fine("ListClassProperties not equal using bag equality:\na: " + as.toString() + "\nb: " + bs.toString());
+ if (!eq) {
+ logger.fine("ListClassProperties not equal using bag equality:\na: " + as.toString() + "\nb: " + bs.toString());
}
return eq;
});
- if(!listMatch) {
- logger.fine("ListProperties don't match: as: " + this.listProperties+ " bs: " + that.listProperties);
+ if (!listMatch) {
+ logger.fine("ListProperties don't match: as: " + this.listProperties + " bs: " + that.listProperties);
logger.fine("ListClassProperties don't match: as: " + this.listClassProperties + " bs: " + that.listClassProperties);
}
@@ -458,9 +344,9 @@ private static class DerefedProperty {
public DerefedProperty(Map confs, SimpleProperty prop, String propName, String locationContext) {
this.propName = propName;
this.locationContext = locationContext;
- this.innerValue = prop.getValue();
+ this.innerValue = prop.value();
// we do it this way because Boolean.parseBoolean doesn't throw like other parse methods
- if(this.innerValue.trim().equalsIgnoreCase("true") || this.innerValue.trim().equalsIgnoreCase("false")) {
+ if (this.innerValue.trim().equalsIgnoreCase("true") || this.innerValue.trim().equalsIgnoreCase("false")) {
this.innerBool = Optional.of(Boolean.parseBoolean(this.innerValue));
} else {
this.innerBool = Optional.empty();
@@ -480,9 +366,9 @@ public DerefedProperty(Map confs, SimpleProperty prop
} catch (DateTimeParseException e) {
this.innerTime = Optional.empty();
}
- Optional maybeDeref = Optional.ofNullable(confs.get(prop.getValue()));
+ Optional maybeDeref = Optional.ofNullable(confs.get(prop.value()));
isDerefed = maybeDeref.isPresent();
- if(isDerefed) {
+ if (isDerefed) {
innerConf = new StructuralConfigurationData(confs, maybeDeref.get());
}
}
@@ -494,18 +380,18 @@ public DerefedProperty(Map confs, SimpleProperty prop
private String reportString() {
return String.format("Property Key: %s%s, with value %s %s %s %s %s", propName, locationContext,
innerValue, (isDerefed ? "(is a reference)" : "(is not a reference)"),
- innerDoubleValue.map(d -> "(parsed as double " + d +")").orElse("(not parsed as double)"),
+ innerDoubleValue.map(d -> "(parsed as double " + d + ")").orElse("(not parsed as double)"),
innerDateTime.map(d -> "(parsed as date " + d + ")").orElse("(not parsed as date)"),
innerBool.map(d -> "(parsed as bool " + d + ")").orElse("(not parsed as bool)"));
}
@Override
public int hashCode() {
- if(this.isDerefed) {
+ if (this.isDerefed) {
return Objects.hash(this.isDerefed, this.innerConf);
- } else if(this.innerBool.isPresent()) {
+ } else if (this.innerBool.isPresent()) {
return Objects.hash(this.isDerefed, this.innerBool.get());
- } else if(this.innerDoubleValue.isPresent()){
+ } else if (this.innerDoubleValue.isPresent()) {
return Objects.hash(this.isDerefed, this.innerDoubleValue.get());
} else if (this.innerDateTime.isPresent()) {
return Objects.hash(this.isDerefed, this.innerDateTime.get());
@@ -530,16 +416,15 @@ public int hashCode() {
*/
@Override
public boolean equals(Object o) {
- if (o instanceof DerefedProperty) {
- DerefedProperty that = (DerefedProperty) o;
- if(this.isDerefed && that.isDerefed) {
+ if (o instanceof DerefedProperty that) {
+ if (this.isDerefed && that.isDerefed) {
return this.innerConf.equals(that.innerConf);
- } else if(!this.isDerefed && !that.isDerefed) {
+ } else if (!this.isDerefed && !that.isDerefed) {
boolean valueMatch;
if (this.innerBool.isPresent() && that.innerBool.isPresent()) {
valueMatch = this.innerBool.get().booleanValue() == that.innerBool.get().booleanValue();
- } else if(this.innerDoubleValue.isPresent() && that.innerDoubleValue.isPresent()) {
+ } else if (this.innerDoubleValue.isPresent() && that.innerDoubleValue.isPresent()) {
valueMatch = Util.doubleEquals(this.innerDoubleValue.get(), that.innerDoubleValue.get());
} else if (this.innerDateTime.isPresent() && that.innerDateTime.isPresent()) {
valueMatch = this.innerDateTime.get().equals(that.innerDateTime.get());
@@ -548,7 +433,7 @@ public boolean equals(Object o) {
} else {
valueMatch = this.innerValue.equals(that.innerValue);
}
- if(!valueMatch) {
+ if (!valueMatch) {
logger.fine(String.format("Property Value mismatch: %s and %s", this.reportString(), that.reportString()));
}
return valueMatch;
@@ -596,86 +481,72 @@ private static boolean innerStructuralEquals(Map a, M
* Checks whether two ConfigurationData objects are 'structurally equal'. Two objects are structurally
* equal when their classNames are the same and when all of their properties are equal or, if those
* properties refer to another ConfigurationData by name, if all of their properties are structurally
- * equal recursively. It does not compare {@link ConfigurationData#serializedForm}, {@link ConfigurationData#importable},
- * {@link ConfigurationData#exportable}, {@link ConfigurationData#leaseTime}, or {@link ConfigurationData#entriesName}.
- * The SerializedForm is not relevant to the equality comparison we are making here, and the remaining fields are
- * all deprecated.
- *
+ * equal recursively. It does not compare {@link ConfigurationData#serializedForm} as the serialized form is not
+ * relevant to the equality comparison we are making here.
*
- *
* N.B. Because the serialized configuration format internally represents things as strings with no type information
* but equality semantics often differ between strings and the types the represent, the method attempts to intuit
* represented types by attempting to parse the string as that type and treating it as that type if it successfully
* parses. This means that, eg. ID values that are typed as strings but are fully numerical will be converted to
* doubles and be compared for equality that way. For more details on the processing see {@link DerefedProperty#equals(Object)}.
- *
*
- *
* {@code aName} should be the name of an element of {@code a} that is to be compared to {@code bName}
* in {@code b}. {@code a} and {@code b} should each contain all the ConfigurationData objects
* needed to instantiate the objects named by {@code aName} and {@code bName} respectively. Objects not
* instantiated by traversing children of {@code aName} and {@code bName} are ignored.
- *
*
- *
* At log-level {@link java.util.logging.Level#FINE} this reports the first configuration object where the two
* instances differ, together with the property values that differ between those instances. Any time this method
* returns {@code false}, it should also log at least one message.
*
- * @param a ConfigurationData List for the first object and its children
- * @param b ConfigurationData List for the second object and its children
+ * @param a ConfigurationData List for the first object and its children
+ * @param b ConfigurationData List for the second object and its children
* @param aName Name of the first object
* @param bName Name of the second object
* @return {@code true} if class and all values of {@code aName} and {@code bName} are the same once
* they have been dereferenced by name according to {@code a} and {@code b}.
*/
- public static boolean structuralEquals(List a, List b , String aName, String bName) {
+ public static boolean structuralEquals(List a, List b, String aName, String bName) {
return innerStructuralEquals(
- a.stream().collect(Collectors.toMap(ConfigurationData::getName, Function.identity())),
- b.stream().collect(Collectors.toMap(ConfigurationData::getName, Function.identity())),
+ a.stream().collect(Collectors.toMap(ConfigurationData::name, Function.identity())),
+ b.stream().collect(Collectors.toMap(ConfigurationData::name, Function.identity())),
aName, bName);
}
/**
* Writes out the configuration data.
+ *
* @param configWriter The writer to use.
* @throws ConfigWriterException If the writer throws an exception.
*/
public void save(ConfigWriter configWriter) throws ConfigWriterException {
- save(configWriter,Collections.emptySet());
+ save(configWriter, Collections.emptySet());
}
/**
- * Writes out the configuration data, redacting (i.e.\ ignoring) fields if necessary.
- * @param configWriter The writer to use.
+ * Writes out the configuration data, redacting (i.e.,\ ignoring) fields if necessary.
+ *
+ * @param configWriter The writer to use.
* @param redactedFields The fields to redact.
* @throws ConfigWriterException If the writer throws an exception.
*/
public void save(ConfigWriter configWriter, Set redactedFields) {
- Map attributes = new HashMap<>();
-
- attributes.put(ConfigLoader.NAME,name);
- attributes.put(ConfigLoader.TYPE,className);
- attributes.put(ConfigLoader.IMPORT,""+isImportable());
- attributes.put(ConfigLoader.EXPORT,""+isExportable());
- if (getLeaseTime() > 0) {
- attributes.put(ConfigLoader.LEASETIME, "" + getLeaseTime());
- }
- if (getSerializedForm() != null) {
- attributes.put(ConfigLoader.SERIALIZED,getSerializedForm());
- }
- if (getEntriesName() != null) {
- attributes.put(ConfigLoader.ENTRIES,getEntriesName());
+ Map attributes = new HashMap<>();
+
+ attributes.put(ConfigLoader.NAME, name);
+ attributes.put(ConfigLoader.TYPE, className);
+ if (serializedForm() != null) {
+ attributes.put(ConfigLoader.SERIALIZED, serializedForm());
}
- Map writtenProperties = new HashMap<>();
- for (Map.Entry p : properties.entrySet()) {
+ Map writtenProperties = new HashMap<>();
+ for (Map.Entry p : properties.entrySet()) {
if (!redactedFields.contains(p.getKey())) {
- writtenProperties.put(p.getKey(),p.getValue());
+ writtenProperties.put(p.getKey(), p.getValue());
}
}
- configWriter.writeComponent(attributes,writtenProperties);
+ configWriter.writeComponent(attributes, writtenProperties);
}
}
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurationManager.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurationManager.java
index 254d3dda..d8cb9cf0 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurationManager.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/ConfigurationManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2023, Oracle and/or its affiliates.
+ * Copyright (c) 2004, 2025, Oracle and/or its affiliates.
*
* Licensed under the 2-clause BSD license.
*
@@ -45,7 +45,6 @@
import com.oracle.labs.mlrg.olcut.util.Pair;
import javax.management.MBeanServer;
-import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -57,10 +56,6 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -83,7 +78,6 @@
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
import static com.oracle.labs.mlrg.olcut.config.PropertySheet.StoredFieldType;
@@ -94,7 +88,7 @@
* @see Configurable
* @see PropertySheet
*/
-public class ConfigurationManager implements Closeable {
+public final class ConfigurationManager {
private static final Logger logger = Logger.getLogger(ConfigurationManager.class.getName());
/**
@@ -103,6 +97,9 @@ public class ConfigurationManager implements Closeable {
public static final char MODULE_SEPARATOR_CHAR = '|';
private static final Pattern WHITESPACE = Pattern.compile("\\s");
+ /**
+ * Default option for configuration file loading.
+ */
public static final Option configFileOption = new Option() {
@Override
public String longName() { return "config-file"; }
@@ -114,6 +111,9 @@ public class ConfigurationManager implements Closeable {
public Class extends Option> annotationType() { return Option.class; }
};
+ /**
+ * Option to provide as an error location when loading from a default configuration file.
+ */
public static final Function defaultConfigOptionFunction = (String path) -> new Option() {
@Override
public char charName() { return '\0'; }
@@ -125,6 +125,9 @@ public class ConfigurationManager implements Closeable {
public Class extends Option> annotationType() { return Option.class; }
};
+ /**
+ * Option for specifying file format factories.
+ */
public static final Option fileFormatOption = new Option() {
@Override
public String longName() { return "config-file-formats"; }
@@ -136,6 +139,9 @@ public class ConfigurationManager implements Closeable {
public Class extends Option> annotationType() { return Option.class; }
};
+ /**
+ * Option for requesting the usage for a CLI application. Equivalent to {@link #helpOption}.
+ */
public static final Option usageOption = new Option() {
@Override
public String longName() { return "usage"; }
@@ -147,6 +153,9 @@ public class ConfigurationManager implements Closeable {
public Class extends Option> annotationType() { return Option.class; }
};
+ /**
+ * Option for requesting the help for a CLI application. Equivalent to {@link #usageOption}.
+ */
public static final Option helpOption = new Option() {
@Override
public String longName() { return "help"; }
@@ -158,30 +167,56 @@ public class ConfigurationManager implements Closeable {
public Class extends Option> annotationType() { return Option.class; }
};
+ /**
+ * Delimiter for arguments.
+ */
public static final char ARG_DELIMITER = ',';
+ /**
+ * Escape character in UNIX.
+ */
public static final char UNIX_ESCAPE_CHAR = '\\';
+ /**
+ * Escape character in Windows.
+ */
public static final char WIN_ESCAPE_CHAR = '^';
- @Deprecated
- public static final char ESCAPE_CHAR = UNIX_ESCAPE_CHAR;
-
+ /**
+ * Is this JVM running on Windows?
+ */
public static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().startsWith("windows");
+ /**
+ * Escape character for the current JVM.
+ */
public static final char CUR_ESCAPE_CHAR = IS_WINDOWS ? WIN_ESCAPE_CHAR : UNIX_ESCAPE_CHAR;
+ /**
+ * Character prefix for overriding a property in a configuration file.
+ */
public static final char CONFIGURABLE_CHAR = '@';
+ /**
+ * Start sequence for a character argument.
+ */
public static final String SHORT_ARG = "-";
+ /**
+ * Start sequence for a long argument.
+ */
public static final String LONG_ARG = "--";
+ /**
+ * Start sequence for a configuration override argument.
+ */
public static final String CONFIGURABLE_OVERRIDE = LONG_ARG + CONFIGURABLE_CHAR;
+ /**
+ * Default empty options instance.
+ */
// **WARNING** - do not convert this into a Lambda, it doesn't work due to reflection issues.
public static final Options EMPTY_OPTIONS = new Options(){ };
- //public static final Options EMPTY_OPTIONS = () -> "";
/**
* Used to support new config file formats at runtime.
@@ -287,12 +322,14 @@ public ConfigurationManager(String[] arguments, String defaultConfigPath) throws
/**
* Creates a new configuration manager.
- *
+ *
* This constructor performs a sequence of operations:
- * - It validates the supplied options struct to make sure it does not have duplicate option names.
- * - Loads any configuration file specified by the {@link ConfigurationManager#configFileOption}.
- * - Parses any configuration overrides and applies them to the configuration manager.
- * - Parses out options for the supplied struct and writes them into the struct.
+ *
+ * - It validates the supplied options struct to make sure it does not have duplicate option names.
+ * - Loads any configuration file specified by the {@link ConfigurationManager#configFileOption}.
+ * - Parses any configuration overrides and applies them to the configuration manager.
+ * - Parses out options for the supplied struct and writes them into the struct.
+ *
* @param arguments An array of command line arguments.
* @param options An object to write the parsed argument values into.
* @throws UsageException Thrown when the user requested the usage string.
@@ -306,12 +343,14 @@ public ConfigurationManager(String[] arguments, Options options) throws UsageExc
/**
* Creates a new configuration manager.
- *
+ *
* This constructor performs a sequence of operations:
- * - It validates the supplied options struct to make sure it does not have duplicate option names.
- * - Loads any configuration file specified by the {@link ConfigurationManager#configFileOption}.
- * - Parses any configuration overrides and applies them to the configuration manager.
- * - Parses out options for the supplied struct and writes them into the struct.
+ *
+ * - It validates the supplied options struct to make sure it does not have duplicate option names.
+ * - Loads any configuration file specified by the {@link ConfigurationManager#configFileOption}.
+ * - Parses any configuration overrides and applies them to the configuration manager.
+ * - Parses out options for the supplied struct and writes them into the struct.
+ *
* @param arguments An array of command line arguments.
* @param options An object to write the parsed argument values into.
* @param useConfigFiles If true, add the config file option. If false ignore the config file option,
@@ -327,12 +366,14 @@ public ConfigurationManager(String[] arguments, Options options, boolean useConf
/**
* Creates a new configuration manager.
- *
+ *
* This constructor performs a sequence of operations:
- * - It validates the supplied options struct to make sure it does not have duplicate option names.
- * - Loads any configuration file specified by the {@link ConfigurationManager#configFileOption}.
- * - Parses any configuration overrides and applies them to the configuration manager.
- * - Parses out options for the supplied struct and writes them into the struct.
+ *
+ * - It validates the supplied options struct to make sure it does not have duplicate option names.
+ * - Loads any configuration file specified by the {@link ConfigurationManager#configFileOption}.
+ * - Parses any configuration overrides and applies them to the configuration manager.
+ * - Parses out options for the supplied struct and writes them into the struct.
+ *
* @param arguments An array of command line arguments.
* @param options An object to write the parsed argument values into.
* @param defaultConfigPath The default config path. Set to empty or null to disable.
@@ -349,13 +390,15 @@ public ConfigurationManager(String[] arguments, Options options, String defaultC
/**
* Creates a new configuration manager.
- *
+ *
* This constructor performs a sequence of operations:
- * - It validates the supplied options struct to make sure it does not have duplicate option names.
- * - Loads any configuration file specified by the {@link ConfigurationManager#configFileOption}.
- * - Loads in the supplied ConfigurationData objects, overwriting things in the files if necessary.
- * - Parses any configuration overrides and applies them to the configuration manager.
- * - Parses out options for the supplied struct and writes them into the struct.
+ *
+ * - It validates the supplied options struct to make sure it does not have duplicate option names.
+ * - Loads any configuration file specified by the {@link ConfigurationManager#configFileOption}.
+ * - Loads in the supplied ConfigurationData objects, overwriting things in the files if necessary.
+ * - Parses any configuration overrides and applies them to the configuration manager.
+ * - Parses out options for the supplied struct and writes them into the struct.
+ *
* @param arguments An array of command line arguments.
* @param configData A list of {@link ConfigurationData} objects.
* @param options An object to write the parsed argument values into.
@@ -410,7 +453,7 @@ public ConfigurationManager(String[] arguments, List configDa
origGlobal = new GlobalProperties(globalProperties);
for (ConfigurationData cd : configData) {
- String instanceName = cd.getName();
+ String instanceName = cd.name();
if (symbolTable.containsKey(instanceName)) {
logger.fine("Overwriting " + instanceName + " loaded from file.");
}
@@ -442,33 +485,15 @@ public ConfigurationManager(String[] arguments, List configDa
// *Must* be last as it can cause Configurable instantiation.
// Throws an exception if there are unknown named arguments at this stage.
try {
- unnamedArguments = AccessController.doPrivileged((PrivilegedExceptionAction) () -> parseOptionArguments(argumentsList, options));
- } catch (PrivilegedActionException e) {
- Exception inner = e.getException();
- if (inner instanceof IllegalAccessException) {
- throw new ArgumentException(e, "Failed to write argument into Options");
- } else if ((inner instanceof InstantiationException) || (inner instanceof NoSuchMethodException) || (inner instanceof InvocationTargetException)) {
+ unnamedArguments = parseOptionArguments(argumentsList, options);
+ } catch (IllegalAccessException e) {
+ throw new ArgumentException(e, "Failed to write argument into Options");
+ } catch (InstantiationException | NoSuchMethodException | InvocationTargetException e) {
throw new ArgumentException(e, "Failed to instantiate a field of Options.");
- } else {
- throw new ArgumentException(inner, "Unexpected exception thrown when reading arguments - " + inner.getMessage());
- }
} catch (PropertyException e) {
throw new ArgumentException(e, e.getMessage() + "\n\n" + usage);
- }
- }
-
- private ConfigurationManager(Map newrpm, GlobalProperties newgp, Map> newSymbolTable, Map newSerializedObjects, GlobalProperties newOrigGlobal) {
- this.configurationDataMap = newrpm;
- this.configurationNameMap = new IdentityHashMap<>();
- this.globalProperties = newgp;
- this.symbolTable = newSymbolTable;
- this.serializedObjects = newSerializedObjects;
- this.origGlobal = newOrigGlobal;
- GlobalProperty sC = globalProperties.get("showCreations");
- if(sC != null) {
- this.showCreations = Boolean.parseBoolean(sC.getValue());
- } else {
- this.showCreations = false;
+ } catch (Exception e) {
+ throw new ArgumentException(e, "Unexpected exception thrown when reading arguments - " + e.getMessage());
}
}
@@ -498,8 +523,13 @@ public static void addFileFormatFactory(FileFormatFactory f) {
formatFactoryMap.put(f.getExtension(),f);
}
- public static FileFormatFactory getFileFormatFactory(String extension) {
- return formatFactoryMap.get(extension);
+ /**
+ * Gets the file format factory for the supplied extension, or {@link Optional#empty()} if it's unknown.
+ * @param extension The extension to check.
+ * @return The file format factory if known.
+ */
+ public static Optional getFileFormatFactory(String extension) {
+ return Optional.ofNullable(formatFactoryMap.get(extension));
}
/**
@@ -608,7 +638,7 @@ public static String validateOptions(Options options, String defaultConfigPath,
// Now check the generic type of the list.
List> list = PropertySheet.getGenericClass(f);
if (list.size() == 1) {
- Class> genericClazz = list.get(0);
+ Class> genericClazz = list.getFirst();
FieldType genericFieldType = FieldType.getFieldType(genericClazz);
if (FieldType.configurableTypes.contains(genericFieldType)) {
throw new ArgumentException(longName,"Argument has a Configurable type, which requires using a config file.");
@@ -652,7 +682,7 @@ public static String validateOptions(Options options, String defaultConfigPath,
/**
* Checks to see if the input after lowercasing is equal to "false" or "true".
- *
+ *
* It's stricter than Boolean.parseBoolean.
* @param input The input to test
* @return True if it's a boolean value, false otherwise.
@@ -663,56 +693,52 @@ private static boolean parseableAsBoolean(String input) {
}
private static URL findURL(String input, String argumentName) {
- return AccessController.doPrivileged((PrivilegedAction)
- () -> {
- int modIndex = input.indexOf(MODULE_SEPARATOR_CHAR);
- if (modIndex > 0) {
- try {
- String witnessClassName = input.substring(0, modIndex);
- String classPathName = input.substring(modIndex + 1);
- Class> witnessClass = Class.forName(witnessClassName);
- URL url = witnessClass.getResource(classPathName);
- if (url != null) {
- return url;
- } // else fall through to regular loading
- } catch (ClassNotFoundException e) {
- // fall through to regular loading
- logger.warning("Failed to load class '" + input.substring(0, modIndex) + "'");
- }
- }
- URL url = ConfigurationManager.class.getResource(input);
- if (url == null) {
- File file = new File(input);
- if (file.exists()) {
- try {
- url = file.toURI().toURL();
- } catch (MalformedURLException e) {
- throw new ArgumentException(e, argumentName, "Can't load config file: " + input);
- }
- } else {
- try {
- url = (new URI(input)).toURL();
- } catch (MalformedURLException | URISyntaxException | IllegalArgumentException e) {
- throw new ArgumentException(argumentName, "Can't find config file: " + input);
- }
- }
- }
- if (IOUtil.isDisallowedProtocol(url)) {
- throw new ConfigLoaderException("Unable to load configurations from URLs with protocol: " + url.getProtocol());
- }
+ int modIndex = input.indexOf(MODULE_SEPARATOR_CHAR);
+ if (modIndex > 0) {
+ try {
+ String witnessClassName = input.substring(0, modIndex);
+ String classPathName = input.substring(modIndex + 1);
+ Class> witnessClass = Class.forName(witnessClassName);
+ URL url = witnessClass.getResource(classPathName);
+ if (url != null) {
return url;
+ } // else fall through to regular loading
+ } catch (ClassNotFoundException e) {
+ // fall through to regular loading
+ logger.warning("Failed to load class '" + input.substring(0, modIndex) + "'");
+ }
+ }
+ URL url = ConfigurationManager.class.getResource(input);
+ if (url == null) {
+ File file = new File(input);
+ if (file.exists()) {
+ try {
+ url = file.toURI().toURL();
+ } catch (MalformedURLException e) {
+ throw new ArgumentException(e, argumentName, "Can't load config file: " + input);
+ }
+ } else {
+ try {
+ url = (new URI(input)).toURL();
+ } catch (MalformedURLException | URISyntaxException | IllegalArgumentException e) {
+ throw new ArgumentException(argumentName, "Can't find config file: " + input);
}
- );
+ }
+ }
+ if (IOUtil.isDisallowedProtocol(url)) {
+ throw new ConfigLoaderException("Unable to load configurations from URLs with protocol: " + url.getProtocol());
+ }
+ return url;
}
/**
* Parses out arguments which override fields in configured objects.
- *
+ *
* Expects arguments of the form --@componentname.propertyname.
- *
+ *
* Throws {@link ArgumentException} if the component name does not match a known component,
* or if the property name is not valid for that class.
- *
+ *
* Removes the parsed arguments from the input, and overwrites values in the rpd for each component.
*
* @param arguments The command line arguments.
@@ -730,14 +756,14 @@ private void parseConfigurableArguments(List arguments) throws ArgumentE
if (split.length == 2) {
ConfigurationData rpd = configurationDataMap.get(split[0]);
if (rpd != null) {
- if (checkConfigurableField(rpd.getClassName(),split[1])) {
+ if (checkConfigurableField(rpd.className(),split[1])) {
// Found a valid configurable field, consume argument.
argsItr.remove();
if (argsItr.hasNext()) {
String param = argsItr.next();
List list = parseStringList(param);
if (list.size() == 1) {
- rpd.add(split[1], new SimpleProperty(list.get(0)));
+ rpd.add(split[1], new SimpleProperty(list.getFirst()));
} else {
rpd.add(split[1], ListProperty.createFromStringList(list));
}
@@ -746,7 +772,7 @@ private void parseConfigurableArguments(List arguments) throws ArgumentE
throw new ArgumentException(curArg,"No parameter for configurable override argument");
}
} else {
- throw new ArgumentException(curArg,"Failed to find field " + split[1] + " in component " + split[0] + " with class " + rpd.getClassName());
+ throw new ArgumentException(curArg,"Failed to find field " + split[1] + " in component " + split[0] + " with class " + rpd.className());
}
} else {
throw new ArgumentException(curArg,"Failed to find component " + split[0]);
@@ -770,7 +796,7 @@ private void parseConfigurableArguments(List arguments) throws ArgumentE
/**
* Parses out the arguments into the supplied {@link Options}.
- *
+ *
* Removes the parsed arguments from the input.
*
* @param arguments The command line arguments.
@@ -801,14 +827,13 @@ private String[] parseOptionArguments(List arguments, Options options) t
}
fields = Options.getOptions(o.getClass());
for (Field f : fields) {
- boolean accessible = f.isAccessible();
f.setAccessible(true);
if (f.get(o) != null) {
logger.fine("Warning: overwriting Options field.");
}
f.set(o,f.getType().getDeclaredConstructor().newInstance());
objectQueue.add((Options)f.get(o));
- f.setAccessible(accessible);
+ f.setAccessible(false);
}
}
@@ -821,35 +846,34 @@ private String[] parseOptionArguments(List arguments, Options options) t
String argName = curArg.substring(2);
Pair arg = longNameMap.get(argName);
if (arg != null) {
- Field f = arg.getA();
+ Field f = arg.a();
FieldType ft = FieldType.getFieldType(f);
// Consume argument.
arguments.remove(i);
consumed = true;
if (i < arguments.size()) {
- boolean accessible = f.isAccessible();
f.setAccessible(true);
String param = arguments.get(i);
List list = parseStringList(param);
if (FieldType.arrayTypes.contains(ft)) {
- f.set(arg.getB(), PropertySheet.parseArrayField(this, curArg, f.getName(), f.getType(), ft, ListProperty.createFromStringList(list)));
+ f.set(arg.b(), PropertySheet.parseArrayField(this, curArg, f.getName(), f.getType(), ft, ListProperty.createFromStringList(list)));
} else if (FieldType.listTypes.contains(ft)) {
List> genericList = PropertySheet.getGenericClass(f);
if (genericList.size() == 1) {
- f.set(arg.getB(), PropertySheet.parseListField(this, curArg, f.getName(), f.getType(), genericList.get(0), ft, ListProperty.createFromStringList(list)));
+ f.set(arg.b(), PropertySheet.parseListField(this, curArg, f.getName(), f.getType(), genericList.get(0), ft, ListProperty.createFromStringList(list)));
} else {
- f.setAccessible(accessible);
+ f.setAccessible(false);
throw new ArgumentException(curArg,"Unknown generic type in argument");
}
} else if (list.size() == 1) {
- f.set(arg.getB(), PropertySheet.parseSimpleField(this,curArg,f.getName(),f.getType(),ft,list.get(0)));
+ f.set(arg.b(), PropertySheet.parseSimpleField(this,curArg,f.getName(),f.getType(),ft,list.get(0)));
} else {
- f.setAccessible(accessible);
+ f.setAccessible(false);
throw new ArgumentException(curArg,"Parsed a list where a single argument was expected. Type = " + f.getType() + ", parsed output = " + list.toString());
}
// Consume parameter.
arguments.remove(i);
- f.setAccessible(accessible);
+ f.setAccessible(false);
} else {
throw new ArgumentException(curArg,"No parameter for argument");
}
@@ -866,17 +890,16 @@ private String[] parseOptionArguments(List arguments, Options options) t
for (int j = 0; j < args.length - 1; j++) {
Pair arg = charNameMap.get(args[j]);
if (arg != null) {
- Field f = arg.getA();
- boolean accessible = f.isAccessible();
+ Field f = arg.a();
f.setAccessible(true);
FieldType ft = FieldType.getFieldType(f);
if (FieldType.isBoolean(ft)) {
- f.set(arg.getB(),true);
+ f.set(arg.b(),true);
} else {
- f.setAccessible(accessible);
+ f.setAccessible(false);
throw new ArgumentException(curArg + " on element " + args[j], "Non boolean argument found where boolean expected");
}
- f.setAccessible(accessible);
+ f.setAccessible(false);
} else {
throw new ArgumentException(curArg + " on element " + args[j], "Unknown argument");
}
@@ -884,8 +907,7 @@ private String[] parseOptionArguments(List arguments, Options options) t
Pair arg = charNameMap.get(args[args.length-1]);
if (arg != null) {
- Field f = arg.getA();
- boolean accessible = f.isAccessible();
+ Field f = arg.a();
f.setAccessible(true);
FieldType ft = FieldType.getFieldType(f);
if (FieldType.isBoolean(ft)) {
@@ -895,14 +917,14 @@ private String[] parseOptionArguments(List arguments, Options options) t
// It only accepts things which lower case to "false" and "true".
String nextArg = arguments.get(i);
if (parseableAsBoolean(nextArg)) {
- f.set(arg.getB(), Boolean.parseBoolean(nextArg));
+ f.set(arg.b(), Boolean.parseBoolean(nextArg));
arguments.remove(i);
} else {
// Next arg is an option or something else, leave unparsed.
- f.set(arg.getB(), true);
+ f.set(arg.b(), true);
}
} else {
- f.set(arg.getB(), true);
+ f.set(arg.b(), true);
}
} else {
// Now we need to accept the next parameter.
@@ -911,29 +933,29 @@ private String[] parseOptionArguments(List arguments, Options options) t
String param = arguments.get(i);
List list = parseStringList(param);
if (FieldType.arrayTypes.contains(ft)) {
- f.set(arg.getB(), PropertySheet.parseArrayField(this, curArg, f.getName(), f.getType(), ft, ListProperty.createFromStringList(list)));
+ f.set(arg.b(), PropertySheet.parseArrayField(this, curArg, f.getName(), f.getType(), ft, ListProperty.createFromStringList(list)));
} else if (FieldType.listTypes.contains(ft)) {
List> genericList = PropertySheet.getGenericClass(f);
if (genericList.size() == 1) {
- f.set(arg.getB(), PropertySheet.parseListField(this, curArg, f.getName(), f.getType(), genericList.get(0), ft, ListProperty.createFromStringList(list)));
+ f.set(arg.b(), PropertySheet.parseListField(this, curArg, f.getName(), f.getType(), genericList.get(0), ft, ListProperty.createFromStringList(list)));
} else {
- f.setAccessible(accessible);
+ f.setAccessible(false);
throw new ArgumentException(curArg,"Unknown generic type in argument");
}
} else if (list.size() == 1) {
- f.set(arg.getB(), PropertySheet.parseSimpleField(this,curArg,f.getName(),f.getType(),ft,list.get(0)));
+ f.set(arg.b(), PropertySheet.parseSimpleField(this,curArg,f.getName(),f.getType(),ft,list.get(0)));
} else {
- f.setAccessible(accessible);
+ f.setAccessible(false);
throw new ArgumentException(curArg,"Parsed a list where a single argument was expected. Type = " + f.getType() + ", parsed output = " + list.toString());
}
// Consume parameter.
arguments.remove(i);
} else {
- f.setAccessible(accessible);
+ f.setAccessible(false);
throw new ArgumentException(curArg,"No parameter for argument");
}
}
- f.setAccessible(accessible);
+ f.setAccessible(false);
}
} else {
throw new ArgumentException(curArg, "Empty argument found.");
@@ -949,7 +971,7 @@ private String[] parseOptionArguments(List arguments, Options options) t
/**
* Parses out the config file argument.
- *
+ *
* Removes the parsed arguments from the input.
* @param arguments The command line arguments.
* @return A list of URLs pointing to olcut config files.
@@ -1169,7 +1191,7 @@ public void addProperties(URL url) throws ConfigLoaderException {
/**
* Overrides a simple property in a specific configurable in this configuration manager.
- *
+ *
* Throws {@link PropertyException} if the configurable/property doesn't exist, has already been instantiated, or doesn't match the field type.
* @param componentName The name of the component.
* @param propertyName The name of the property/field.
@@ -1179,7 +1201,7 @@ public void overrideConfigurableProperty(String componentName, String propertyNa
ConfigurationData rpd = configurationDataMap.get(componentName);
if (rpd != null) {
if (!symbolTable.containsKey(componentName)) {
- StoredFieldType type = getStoredFieldType(rpd.getClassName(), propertyName);
+ StoredFieldType type = getStoredFieldType(rpd.className(), propertyName);
if ((type == StoredFieldType.STRING) && (value instanceof SimpleProperty)) {
rpd.add(propertyName, value);
} else if ((type == StoredFieldType.LIST) && (value instanceof ListProperty)) {
@@ -1187,7 +1209,7 @@ public void overrideConfigurableProperty(String componentName, String propertyNa
} else if ((type == StoredFieldType.MAP) && (value instanceof MapProperty)) {
rpd.add(propertyName, value);
} else if (type == StoredFieldType.NONE) {
- throw new PropertyException(componentName, propertyName, "Failed to find field " + propertyName + " in component " + componentName + " with class " + rpd.getClassName());
+ throw new PropertyException(componentName, propertyName, "Failed to find field " + propertyName + " in component " + componentName + " with class " + rpd.className());
} else {
throw new PropertyException(componentName, propertyName, "Incompatible field type, found " + type + ", expected " + value.getClass().getSimpleName());
}
@@ -1199,12 +1221,6 @@ public void overrideConfigurableProperty(String componentName, String propertyNa
}
}
- /**
- * Shuts down the configuration manager, which is a no-op on the standard version.
- */
- @Override
- public synchronized void close() { }
-
/**
* Get a copy of any unnamed arguments that weren't parsed into an {@link Options}
* instance, or used to override a {@link Configurable} field.
@@ -1216,7 +1232,7 @@ public String[] getUnnamedArguments() {
/**
* Gets the configuration data associated with a given instance.
- *
+ *
* Allows the modification of configuration for future objects.
* @param instanceName the name of the instance whose properties we want
* @return the associated configuration data, or {@link Optional#empty} if there is no data
@@ -1224,16 +1240,12 @@ public String[] getUnnamedArguments() {
*/
public Optional getConfigurationData(String instanceName) {
ConfigurationData data = configurationDataMap.get(instanceName);
- if (data == null) {
- return Optional.empty();
- } else {
- return Optional.of(data);
- }
+ return Optional.ofNullable(data);
}
/**
* Does this ConfigurationManager know about an instance called instanceName.
- *
+ *
* Does not trigger class instantiation or configuration.
* @param instanceName The name to check.
* @return True if it contains a {@link Configurable} called instanceName.
@@ -1265,23 +1277,23 @@ public Optional getConfiguredName(Configurable conf) {
* @return the property sheet for the object.
*/
@SuppressWarnings("unchecked") // Warning suppressed as it's behind an isAssignableFrom check.
- protected PropertySheet extends Configurable> getPropertySheet(String instanceName) {
+ private PropertySheet extends Configurable> getPropertySheet(String instanceName) {
if(!symbolTable.containsKey(instanceName)) {
// if it is not in the symbol table, so construct
// it based upon our raw property data
ConfigurationData rpd = configurationDataMap.get(instanceName);
if(rpd != null) {
- String className = rpd.getClassName();
+ String className = rpd.className();
try {
Class> confClass = Class.forName(className);
if (Configurable.class.isAssignableFrom(confClass)) {
PropertySheet extends Configurable> propertySheet = new PropertySheet<>((Class extends Configurable>)confClass,this,rpd);
symbolTable.put(instanceName, propertySheet);
} else {
- throw new PropertyException(rpd.getName(), "Class " + className + " does not implement Configurable.");
+ throw new PropertyException(rpd.name(), "Class " + className + " does not implement Configurable.");
}
} catch (ClassNotFoundException e) {
- throw new PropertyException(e, rpd.getName(), "Class " + className + " not found");
+ throw new PropertyException(e, rpd.name(), "Class " + className + " not found");
}
}
}
@@ -1291,7 +1303,7 @@ protected PropertySheet extends Configurable> getPropertySheet(String instance
/**
* Gets all instances that are of the given type.
- *
+ *
* Only returns the names of the instantiated objects.
*
* @param type the desired type of instance
@@ -1431,8 +1443,7 @@ private Configurable innerLookup(String instanceName, ComponentListener cl, bool
configurationNameMap.put(ret, instanceName);
}
- if (ret instanceof Startable) {
- Startable stret = (Startable) ret;
+ if (ret instanceof Startable stret) {
Thread t = new Thread(stret);
t.setName(instanceName + "_thread");
stret.setThread(t);
@@ -1472,7 +1483,7 @@ public T lookup(Class c, ComponentListener cl) {
return null;
}
Collections.shuffle(comps);
- return comps.get(0);
+ return comps.getFirst();
}
/**
@@ -1495,8 +1506,7 @@ public Map lookupAllMap(Class c) {
if(!c.isInterface()) {
String className = c.getName();
for (Map.Entry e : configurationDataMap.entrySet()) {
- if (e.getValue().getClassName().equals(className) &&
- !e.getValue().isImportable()) {
+ if (e.getValue().className().equals(className)) {
ret.put(e.getKey(),(T)lookup(e.getKey()));
}
}
@@ -1506,8 +1516,8 @@ public Map lookupAllMap(Class c) {
// implementing classes and return them.
for (Map.Entry e : configurationDataMap.entrySet()) {
try {
- Class clazz = Class.forName(e.getValue().getClassName());
- if (!e.getValue().isImportable() && c.isAssignableFrom(clazz) && !clazz.isInterface()) {
+ Class> clazz = Class.forName(e.getValue().className());
+ if (c.isAssignableFrom(clazz) && !clazz.isInterface()) {
ret.put(e.getKey(),(T)innerLookup(e.getKey(),null,true));
}
} catch (ClassNotFoundException ex) {
@@ -1531,13 +1541,11 @@ public List lookupAll(Class c) {
//
// If the class isn't an interface, then lookup each of the names
- // in the raw property data with the given class
- // name, ignoring those things marked as importable.
+ // in the raw property data with the given class name.
if(!c.isInterface()) {
String className = c.getName();
for (Map.Entry e : configurationDataMap.entrySet()) {
- if (e.getValue().getClassName().equals(className) &&
- !e.getValue().isImportable()) {
+ if (e.getValue().className().equals(className)) {
ret.add((T)lookup(e.getKey()));
}
}
@@ -1547,8 +1555,8 @@ public List lookupAll(Class c) {
// implementing classes and return them.
for (Map.Entry e : configurationDataMap.entrySet()) {
try {
- Class clazz = Class.forName(e.getValue().getClassName());
- if (!e.getValue().isImportable() && c.isAssignableFrom(clazz) && !clazz.isInterface()) {
+ Class> clazz = Class.forName(e.getValue().className());
+ if (c.isAssignableFrom(clazz) && !clazz.isInterface()) {
ret.add((T)innerLookup(e.getKey(),null,true));
}
} catch (ClassNotFoundException ex) {
@@ -1594,15 +1602,14 @@ public T lookupSingleton(Class c, boolean allowAssig
for(Map.Entry e : configurationDataMap.entrySet()) {
ConfigurationData rpd = e.getValue();
try {
- Class> pclass = Class.forName(rpd.getClassName());
- if (!rpd.isImportable() &&
- ((allowAssignable && c.isAssignableFrom(pclass)) ||
- (!allowAssignable && rpd.getClassName().equals(c.getName())))) {
+ Class> pclass = Class.forName(rpd.className());
+ if ((allowAssignable && c.isAssignableFrom(pclass)) ||
+ (!allowAssignable && rpd.className().equals(c.getName()))) {
instanceNames.add(e.getKey());
}
} catch(ClassNotFoundException ex) {
logger.warning(String.format("No class %s found in ConfigurationManager",
- rpd.getClassName()));
+ rpd.className()));
}
}
@@ -1616,10 +1623,10 @@ public T lookupSingleton(Class c, boolean allowAssig
throw new PropertyException("", "Multiple instances of " + c.getName() + " found in configuration: " + names);
}
- String matchedName = instanceNames.get(0);
+ String matchedName = instanceNames.getFirst();
ConfigurationData cd = configurationDataMap.get(matchedName);
try {
- Class> matchedClass = Class.forName(cd.getClassName());
+ Class> matchedClass = Class.forName(cd.className());
if (!matchedClass.isInterface()) {
return (T)lookup(matchedName);
} else {
@@ -1645,13 +1652,13 @@ public List listAll(Class c) {
for(Map.Entry e : configurationDataMap.entrySet()) {
ConfigurationData rpd = e.getValue();
try {
- Class pclass = Class.forName(rpd.getClassName());
+ Class> pclass = Class.forName(rpd.className());
if (c.isAssignableFrom(pclass)) {
ret.add(e.getKey());
}
} catch(ClassNotFoundException ex) {
logger.warning(String.format("No class %s found in ConfigurationManager",
- rpd.getClassName()));
+ rpd.className()));
}
}
@@ -1701,10 +1708,19 @@ public boolean removeConfigurable(String name) {
}
}
+ /**
+ * Adds all the configurations and global properties from the supplied CM to this CM.
+ * @param subCM The CM containing configurations to copy.
+ */
public void addSubConfiguration(ConfigurationManager subCM) {
addSubConfiguration(subCM, false);
}
+ /**
+ * Adds all the configurations and global properties from the supplied CM to this CM.
+ * @param subCM The CM containing configurations to copy.
+ * @param overwrite Should elements of this CM be overwritten if they have the same name?
+ */
public void addSubConfiguration(ConfigurationManager subCM, boolean overwrite) {
Collection compNames = getComponentNames();
@@ -1725,8 +1741,7 @@ public void addSubConfiguration(ConfigurationManager subCM, boolean overwrite) {
globalProperties.putAll(subCM.globalProperties);
for(Map.Entry> e : subCM.symbolTable.entrySet()) {
- PropertySheet extends Configurable> newPS = e.getValue().copy();
- newPS.setCM(this);
+ PropertySheet extends Configurable> newPS = e.getValue().copy(this);
symbolTable.put(e.getKey(),newPS);
}
@@ -1743,7 +1758,7 @@ public void addSubConfiguration(ConfigurationManager subCM, boolean overwrite) {
* this configuration manager instance.
*/
public void addConfiguration(ConfigurationData newData) {
- String instanceName = newData.getName();
+ String instanceName = newData.name();
if (symbolTable.containsKey(instanceName)) {
throw new IllegalArgumentException("tried to override existing instantiated component name");
}
@@ -1753,7 +1768,7 @@ public void addConfiguration(ConfigurationData newData) {
/**
* Adds all the configurations in the list to this ConfigurationManager.
- *
+ *
* Does not trigger class loading or validation of the ConfigurationData objects.
* @param newData The configurations to ingest.
*/
@@ -1819,7 +1834,7 @@ public void setGlobalProperty(String propertyName, String value) {
}
}
- protected String getStrippedComponentName(String propertyName) {
+ private String getStrippedComponentName(String propertyName) {
assert propertyName != null;
while(propertyName.startsWith("$")) {
@@ -1838,12 +1853,10 @@ protected String getStrippedComponentName(String propertyName) {
*/
@Override
public boolean equals(Object obj) {
- if(!(obj instanceof ConfigurationManager)) {
+ if(!(obj instanceof ConfigurationManager cm)) {
return false;
}
- ConfigurationManager cm = (ConfigurationManager) obj;
-
Collection setA = new HashSet<>(getComponentNames());
Collection setB = new HashSet<>(cm.getComponentNames());
if(!setA.equals(setB)) {
@@ -1871,7 +1884,7 @@ public int hashCode() {
/**
* Saves the current configuration to the given file.
- *
+ *
* Only writes out instantiated components, and redacts their fields if required.
*
* @param file Place to save the configuration.
@@ -1939,7 +1952,7 @@ public void save(OutputStream writer, String extension, boolean writeAll) throws
* will be written.
* @throws ConfigWriterException If an error occurs while writing the configuration.
*/
- protected void write(ConfigWriter writer, boolean writeAll) throws ConfigWriterException {
+ private void write(ConfigWriter writer, boolean writeAll) throws ConfigWriterException {
writer.writeStartDocument();
//
// Write out the global properties.
@@ -1985,7 +1998,7 @@ protected void write(ConfigWriter writer, boolean writeAll) throws ConfigWriterE
writer.close();
}
- protected PropertySheet createPropertySheet(T configurable, ConfigurationManager cm, ConfigurationData rpd) {
+ private PropertySheet createPropertySheet(T configurable, ConfigurationManager cm, ConfigurationData rpd) {
return new PropertySheet<>(configurable,cm,rpd);
}
@@ -2005,23 +2018,22 @@ public String importConfigurable(Configurable configurable) throws PropertyExcep
try {
Set fields = PropertySheet.getAllFields(configurable.getClass());
for (Field field : fields) {
- boolean accessible = field.isAccessible();
field.setAccessible(true);
ConfigurableName nameAnnotation = field.getAnnotation(ConfigurableName.class);
if (nameAnnotation != null) {
configName = (String) field.get(configurable);
//
// break out of loop at the first instance of ConfigurableName.
- field.setAccessible(accessible);
+ field.setAccessible(false);
break;
}
- field.setAccessible(accessible);
+ field.setAccessible(false);
}
} catch (IllegalAccessException ex) {
- throw new PropertyException(ex, configName, "Failed to read the ConfigurableName field");
+ throw new PropertyException(ex, configName, "Failed to read the @ConfigurableName field");
}
- if (configName.equals("")) {
+ if (configName.isEmpty()) {
throw new PropertyException("", "Failed to extract name from @ConfigurableName field");
} else {
return importConfigurable(configurable, configName);
@@ -2034,7 +2046,7 @@ public String importConfigurable(Configurable configurable) throws PropertyExcep
* this configuration manager. This is useful in situations where you have
* a configurable component but you don't have the property sheet that
* generated it (e.g., if it was sent over the network).
- *
+ *
* It's best effort, if your object graph is loopy, it will flatten it
* into a tree by cloning elements.
*
@@ -2064,7 +2076,6 @@ public String importConfigurable(Configurable configurable,
try {
Set fields = PropertySheet.getAllFields(confClass);
for (Field field : fields) {
- boolean accessible = field.isAccessible();
field.setAccessible(true);
Config configAnnotation = field.getAnnotation(Config.class);
if (configAnnotation != null) {
@@ -2171,7 +2182,7 @@ public String importConfigurable(Configurable configurable,
});
}
}
- field.setAccessible(accessible);
+ field.setAccessible(false);
}
ConfigurationData rpd = new ConfigurationData(name, confClass.getName(), m);
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/DescribeConfigurable.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/DescribeConfigurable.java
index 3536ac66..79c8d86a 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/DescribeConfigurable.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/DescribeConfigurable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2021, Oracle and/or its affiliates.
+ * Copyright (c) 2004, 2025, Oracle and/or its affiliates.
*
* Licensed under the 2-clause BSD license.
*
@@ -42,12 +42,14 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -222,8 +224,6 @@ private static String generateDefaultValue(FieldInfo fi) {
return "/path/to/a/file";
case URL:
return "file:///path/to/a/file";
- case RANDOM:
- return "42";
case ENUM:
try {
return Class.forName(fi.className).getEnumConstants()[0].toString();
@@ -249,10 +249,9 @@ public static SortedMap generateFieldInfo(Class extends Con
Object instance;
try {
Constructor extends Configurable> constructor = configurableClass.getDeclaredConstructor();
- boolean isAccessible = constructor.isAccessible();
constructor.setAccessible(true);
instance = constructor.newInstance();
- constructor.setAccessible(isAccessible);
+ constructor.setAccessible(false);
} catch (NoSuchMethodException ex) {
throw new IllegalStateException("No-args constructor not found for class " + configurableClass, ex);
} catch (InvocationTargetException | IllegalAccessException | InstantiationException ex) {
@@ -263,7 +262,6 @@ public static SortedMap generateFieldInfo(Class extends Con
for (Field f : fieldSet) {
Config configAnnotation = f.getAnnotation(Config.class);
if (configAnnotation != null) {
- boolean accessible = f.isAccessible();
f.setAccessible(true);
Object extractedField = null;
try {
@@ -283,7 +281,7 @@ public static SortedMap generateFieldInfo(Class extends Con
if (FieldType.listTypes.contains(ft)) {
List> genericList = PropertySheet.getGenericClass(f);
if (genericList.size() == 1) {
- Class> listType = genericList.get(0);
+ Class> listType = genericList.getFirst();
FieldInfo fi;
if (listType.isEnum()) {
Object[] constants = listType.getEnumConstants();
@@ -325,7 +323,7 @@ public static SortedMap generateFieldInfo(Class extends Con
map.put(f.getName(), fi);
}
}
- f.setAccessible(accessible);
+ f.setAccessible(false);
}
}
@@ -394,11 +392,11 @@ public static List> generateDescription(Map map)
* @param map The field infos for that class.
*/
public static void writeExampleConfig(OutputStream stream, String fileFormat, Class extends Configurable> configurableClass, Map map) {
- FileFormatFactory factory = ConfigurationManager.getFileFormatFactory(fileFormat);
- if (factory == null) {
+ Optional factory = ConfigurationManager.getFileFormatFactory(fileFormat);
+ if (factory.isEmpty()) {
throw new IllegalArgumentException("No format factory found for extension '" + fileFormat + "'");
}
- ConfigWriter configWriter = factory.getWriter(stream);
+ ConfigWriter configWriter = factory.get().getWriter(stream);
// Generate attributes
Map attributes = new HashMap<>();
@@ -544,7 +542,7 @@ public static void main(String[] args) throws UnsupportedEncodingException {
writeExampleConfig(writer, o.extension, configurableClass, map);
- System.out.println("Example :\n" + writer.toString("UTF-8"));
+ System.out.println("Example :\n" + writer.toString(StandardCharsets.UTF_8));
}
} else {
logger.warning("The supplied class did not implement Configurable, class = " + clazz.getCanonicalName());
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/FieldType.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/FieldType.java
index 7211da3d..18f9cc75 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/FieldType.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/FieldType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2021, Oracle and/or its affiliates.
+ * Copyright (c) 2004, 2025, Oracle and/or its affiliates.
*
* Licensed under the 2-clause BSD license.
*
@@ -89,9 +89,7 @@ public enum FieldType {
DATE_TIME(OffsetDateTime.class),
DATE(LocalDate.class),
TIME(OffsetTime.class),
- ENUM(Enum.class),
- @Deprecated
- RANDOM(Random.class);
+ ENUM(Enum.class);
private static final Class> configurableClass = Configurable.class;
private static final Class> configurableArrayClass = Configurable[].class;
@@ -110,7 +108,7 @@ public enum FieldType {
public final static EnumSet simpleTypes = EnumSet.of(BOOLEAN, BYTE, CHAR, SHORT, INTEGER, LONG, FLOAT, DOUBLE, STRING,
CONFIGURABLE, ATOMIC_INTEGER,
- ATOMIC_LONG, FILE, PATH, URL, RANDOM, DATE_TIME, DATE, TIME, ENUM);
+ ATOMIC_LONG, FILE, PATH, URL, DATE_TIME, DATE, TIME, ENUM);
public final static EnumSet mapTypes = EnumSet.of(MAP);
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/InternalConfigurationException.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/InternalConfigurationException.java
index cf75da6c..754e1d3b 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/InternalConfigurationException.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/InternalConfigurationException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2020, Oracle and/or its affiliates.
+ * Copyright (c) 2004, 2025, Oracle and/or its affiliates.
*
* Licensed under the 2-clause BSD license.
*
@@ -29,20 +29,16 @@
package com.oracle.labs.mlrg.olcut.config;
/**
- *
* Indicates that a problem occurred while setting one or more properties for this component. This includes errors as
* improper type for component(-lists) properties, out-of-range-problems for double-, int- and ranged string-properties,
* instantiation errors and undefined mandatory properties.
- *
*
* This exception is instantiable only by the configuration management classes itself. In order to indicate problems
* within Configurable.newProperties which are not coped by types or ranges (eg file-not-found, complex configuration
* logic problems, etc.) PropertyException (which superclasses this class) can be used.
- *
*
* The intention of the class is to make a clear distinction between core configuration errors and high level user
* specific problems.
- *
*/
public class InternalConfigurationException extends PropertyException {
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Option.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Option.java
index fee57fce..e2f7ae28 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Option.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Option.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2020, Oracle and/or its affiliates.
+ * Copyright (c) 2004, 2025, Oracle and/or its affiliates.
*
* Licensed under the 2-clause BSD license.
*
@@ -36,10 +36,10 @@
/**
* An annotation that can be added directly to variables that are command line arguments.
- *
+ *
* Defaults to the value set during construction. Supports all the types supported
* by the {@link Config} annotation.
- *
+ *
* Option should not be applied to a static field.
*/
@Documented
@@ -47,13 +47,32 @@
@Target(ElementType.FIELD)
public @interface Option {
+ /**
+ * Character option, allowed to be any single UTF-8 glyph.
+ * @return The character for this option.
+ */
char charName() default EMPTY_CHAR;
+ /**
+ * Long name of the option. Allowed to be any sequence of non-whitespace characters.
+ * @return The long name.
+ */
String longName();
+ /**
+ * Usage field for the option, displayed in the help message.
+ * @return The option's usage text.
+ */
String usage();
+ /**
+ * Empty character.
+ */
public static final char EMPTY_CHAR = '\0';
+
+ /**
+ * Space character.
+ */
public static final char SPACE_CHAR = ' ';
}
diff --git a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Options.java b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Options.java
index 67c9d8ac..59ff41cf 100644
--- a/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Options.java
+++ b/olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/config/Options.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2020, Oracle and/or its affiliates.
+ * Copyright (c) 2004, 2025, Oracle and/or its affiliates.
*
* Licensed under the 2-clause BSD license.
*
@@ -36,8 +36,6 @@
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -72,7 +70,7 @@ public interface Options {
/**
* Gets a possibly multi line description of this Options subclass.
- *
+ *
* Default implementation returns the empty string.
* @return A description string.
*/
@@ -135,7 +133,7 @@ public static List> getUsage(Class extends Options> options) {
ArrayList> list = new ArrayList<>();
ArrayList> optionsList = new ArrayList<>();
Set fields = getOptionFields(options);
- if (fields.size() == 0) {
+ if (fields.isEmpty()) {
return list;
} else {
list.add(new ArrayList<>(Collections.singletonList(options.getSimpleName())));
@@ -214,8 +212,7 @@ public static String generateTypeDescription(Field f) {
return "enum - " + getEnumConstantString(enumClazz);
} else if (clazz == EnumSet.class) {
Type type = f.getGenericType();
- if (type instanceof ParameterizedType) {
- ParameterizedType typeName = (ParameterizedType) type;
+ if (type instanceof ParameterizedType typeName) {
// Should only have a single type parameter
Type enumType = typeName.getActualTypeArguments()[0];
try {
@@ -283,19 +280,16 @@ public static ArrayList getOptionUsage(Option option, Field f, Options o
}
output.add(option.longName());
output.add(type);
- Object extractedField = AccessController.doPrivileged((PrivilegedAction