diff options
author | gjoranv <gv@verizonmedia.com> | 2021-02-17 16:16:19 +0100 |
---|---|---|
committer | gjoranv <gv@verizonmedia.com> | 2021-02-17 17:13:44 +0100 |
commit | da183fe82e5d9eaccf3cbf03a0751cc74851ec31 (patch) | |
tree | 70cb9d97a7d2bdd5785e7512d7f88d5823e1407e /container-core/src/main/java/com/yahoo/processing/request/Properties.java | |
parent | a0ae5022c689578e456eba2b5f89ac077e0b07e1 (diff) |
Add java source files from the processing module.
Diffstat (limited to 'container-core/src/main/java/com/yahoo/processing/request/Properties.java')
-rw-r--r-- | container-core/src/main/java/com/yahoo/processing/request/Properties.java | 634 |
1 files changed, 634 insertions, 0 deletions
diff --git a/container-core/src/main/java/com/yahoo/processing/request/Properties.java b/container-core/src/main/java/com/yahoo/processing/request/Properties.java new file mode 100644 index 00000000000..9362de59203 --- /dev/null +++ b/container-core/src/main/java/com/yahoo/processing/request/Properties.java @@ -0,0 +1,634 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.processing.request; + +import java.util.Map; +import java.util.HashMap; +import java.util.Collections; + +/** + * The properties of a request + * + * @author bratseth + */ +public class Properties implements Cloneable { + + private final static CloneHelper cloneHelper = new CloneHelper(); + private Properties chained = null; + + /** + * Sets the properties chained to this. + * + * @param chained the properties to chain to this, or null to make this the last in the chain + * @return the given chained object to allow setting up a chain by dotting in one statement + */ + public Properties chain(Properties chained) { + this.chained = chained; + return chained; + } + + /** + * Returns the properties chained to this, or null if this is the last in the chain + */ + public Properties chained() { + return chained; + } + + /** + * Returns the first instance of the given class in this chain, or null if none + */ + @SuppressWarnings("unchecked") + public final <T extends Properties> T getInstance(Class<T> propertyClass) { + if (propertyClass.isAssignableFrom(this.getClass())) return (T) this; + if (chained == null) return null; + return chained.getInstance(propertyClass); + } + + /** + * Lists all properties of this with no context, by delegating to listProperties("") + */ + public final Map<String, Object> listProperties() { + return listProperties(CompoundName.empty); + } + + /** + * Returns a snapshot of all properties of this - same as listProperties("",context) + */ + public final Map<String, Object> listProperties(Map<String, String> context) { + return listProperties(CompoundName.empty, context, this); + } + + /** + * Returns a snapshot of all properties by calling listProperties(path,null) + */ + public final Map<String, Object> listProperties(CompoundName path) { + return listProperties(path, null, this); + } + + /** + * Returns a snapshot of all properties by calling listProperties(path,null) + */ + public final Map<String, Object> listProperties(String path) { + return listProperties(new CompoundName(path), null, this); + } + + /** + * Returns a snapshot of all properties by calling listProperties(path,null) + */ + public final Map<String, Object> listProperties(CompoundName path, Map<String, String> context) { + return listProperties(path, context, this); + } + + /** + * Returns a snapshot of all properties by calling listProperties(path,null) + */ + public final Map<String, Object> listProperties(String path, Map<String, String> context) { + return listProperties(new CompoundName(path), context, this); + } + + /** + * Returns a snapshot of all properties of this having a given path prefix + * <p> + * Some sources of properties may not be list-able (e.g those using reflection) + * and will not be included in this snapshot. + * + * + * @param path the prefix (up to a ".") of the properties to return, or null or the empty string to return all properties + * @param context the context used to resolve the properties, or null if none + * @param substitution the properties which will be used to do string substitution in the values added to the map + */ + public Map<String, Object> listProperties(CompoundName path, Map<String, String> context, Properties substitution) { + if (path == null) + path = CompoundName.empty; + if (chained() == null) + return new HashMap<>(); + else + return chained().listProperties(path, context, substitution); + } + + /** + * Returns a snapshot of all properties of this having a given path prefix + * <p> + * Some sources of properties may not be list-able (e.g those using reflection) + * and will not be included in this snapshot. + * + * + * @param path the prefix (up to a ".") of the properties to return, or null or the empty string to return all properties + * @param context the context used to resolve the properties, or null if none + * @param substitution the properties which will be used to do string substitution in the values added to the map + */ + public final Map<String, Object> listProperties(String path, Map<String, String> context, Properties substitution) { + return listProperties(new CompoundName(path), context, substitution); + } + + /** + * Gets a named value which (if necessary) is resolved using a property context. + * + * @param name the name of the property to return + * @param context the variant resolution context, or null if none + * @param substitution the properties used to substitute in these properties, or null if none + */ + public Object get(CompoundName name, Map<String, String> context, Properties substitution) { + if (chained == null) return null; + return chained.get(name, context, substitution); + } + + /** + * Gets a named value which (if necessary) is resolved using a property context + * + * @param name the name of the property to return + * @param context the variant resolution context, or null if none + * @param substitution the properties used to substitute in these properties, or null if none + */ + public final Object get(String name, Map<String, String> context, Properties substitution) { + return get(new CompoundName(name), context, substitution); + } + + /** + * Gets a named value from the first chained instance which has one by calling get(name,context,this) + */ + public final Object get(CompoundName name, Map<String, String> context) { + return get(name, context, this); + } + + /** + * Gets a named value from the first chained instance which has one by calling get(name,context,this) + */ + public final Object get(String name, Map<String, String> context) { + return get(new CompoundName(name), context, this); + } + + /** + * Gets a named value from the first chained instance which has one by calling get(name,null,this) + */ + public final Object get(CompoundName name) { + return get(name, null, this); + } + + /** + * Gets a named value from the first chained instance which has one by calling get(name,null,this) + */ + public final Object get(String name) { + return get(new CompoundName(name), null, this); + } + + /** + * Gets a named value from the first chained instance which has one, + * or the default value if no value is set, or if the first value encountered is explicitly set to null. + * <p> + * This default implementation simply forwards to the chained instance, or returns the default if none + * + * + * @param name the name of the property to return + * @param defaultValue the default value returned if the value returned is null + */ + public final Object get(CompoundName name, Object defaultValue) { + Object value = get(name); + if (value == null) return defaultValue; + return value; + } + + /** + * Gets a named value from the first chained instance which has one, + * or the default value if no value is set, or if the first value encountered is explicitly set to null. + * <p> + * This default implementation simply forwards to the chained instance, or returns the default if none + * + * @param name the name of the property to return + * @param defaultValue the default value returned if the value returned is null + */ + public final Object get(String name, Object defaultValue) { + return get(new CompoundName(name), defaultValue); + } + + /** + * Sets a value to the first chained instance which accepts it. + * <p> + * This default implementation forwards to the chained instance or throws + * a RuntimeException if there is not chained instance. + * + * @param name the name of the property + * @param value the value to set. Setting a property to null clears it. + * @param context the context used to resolve where the values should be set, or null if none + * @throws RuntimeException if no instance in the chain accepted this name-value pair + */ + public void set(CompoundName name, Object value, Map<String, String> context) { + if (chained == null) throw new RuntimeException("Property '" + name + "->" + + value + "' was not accepted in this property chain"); + chained.set(name, value, context); + } + + /** + * Sets a value to the first chained instance which accepts it. + * <p> + * This default implementation forwards to the chained instance or throws + * a RuntimeException if there is not chained instance. + * + * @param name the name of the property + * @param value the value to set. Setting a property to null clears it. + * @param context the context used to resolve where the values should be set, or null if none + * @throws RuntimeException if no instance in the chain accepted this name-value pair + */ + public final void set(String name, Object value, Map<String, String> context) { + set(new CompoundName(name), value, context); + } + + /** + * Sets a value to the first chained instance which accepts it by calling set(name,value,null). + * + * @param name the name of the property + * @param value the value to set. Setting a property to null clears it. + * @throws RuntimeException if no instance in the chain accepted this name-value pair + */ + public final void set(CompoundName name, Object value) { + set(name, value, null); + } + + /** + * Sets a value to the first chained instance which accepts it by calling set(name,value,null). + * + * @param name the name of the property + * @param value the value to set. Setting a property to null clears it. + * @throws RuntimeException if no instance in the chain accepted this name-value pair + */ + public final void set(String name, Object value) { + set(new CompoundName(name), value, Collections.<String,String>emptyMap()); + } + + /** + * Sets all properties having this name as a compound prefix to null. + * I.e clearAll("a") will clear the value of "a" and "a.b" but not "ab". + * This default implementation forwards to the chained instance or throws + * a RuntimeException if there is not chained instance. + * + * @param name the compound prefix of the properties to clear + * @param context the context used to resolve where the values should be cleared, or null if none + * @throws RuntimeException if no instance in the chain accepted this name-value pair + */ + public void clearAll(CompoundName name, Map<String, String> context) { + if (chained == null) throw new RuntimeException("Property '" + name + + "' was not accepted in this property chain"); + chained.clearAll(name, context); + } + + /** + * Sets all properties having this name as a compound prefix to null. + * I.e clearAll("a") will clear the value of "a" and "a.b" but not "ab". + * + * @param name the compound prefix of the properties to clear + * @param context the context used to resolve where the values should be cleared, or null if none + * @throws RuntimeException if no instance in the chain accepted this name-value pair + */ + public final void clearAll(String name, Object value, Map<String, String> context) { + set(new CompoundName(name), value, context); + } + + /** + * Sets all properties having this name as a compound prefix to null. + * I.e clearAll("a") will clear the value of "a" and "a.b" but not "ab". + * + * @param name the compound prefix of the properties to clear + * @throws RuntimeException if no instance in the chain accepted this name-value pair + */ + public final void clearAll(CompoundName name) { + clearAll(name, null); + } + + /** + * Sets all properties having this name as a compound prefix to null. + * I.e clearAll("a") will clear the value of "a" and "a.b" but not "ab". + * + * @param name the compound prefix of the properties to clear + * @throws RuntimeException if no instance in the chain accepted this name-value pair + */ + public final void clearAll(String name) { + clearAll(new CompoundName(name), Collections.<String,String>emptyMap()); + } + + /** + * Gets a property as a boolean - if this value can reasonably be interpreted as a boolean, this will return + * the value. Returns false if this property is null. + */ + public final boolean getBoolean(CompoundName name) { + return getBoolean(name, false); + } + + /** + * Gets a property as a boolean - if this value can reasonably be interpreted as a boolean, this will return + * the value. Returns false if this property is null. + */ + public final boolean getBoolean(String name) { + return getBoolean(name, false); + } + + /** + * Gets a property as a boolean. + * This will return true only if the value is either the empty string, + * or any Object which has a toString which is case-insensitive equal to "true" + * + * @param defaultValue the value to return if this property is null + */ + public final boolean getBoolean(CompoundName key, boolean defaultValue) { + return asBoolean(get(key), defaultValue); + } + + /** + * Gets a property as a boolean. + * This will return true only if the value is either the empty string, + * or any Object which has a toString which is case-insensitive equal to "true" + * + * @param defaultValue the value to return if this property is null + */ + public final boolean getBoolean(String key, boolean defaultValue) { + return asBoolean(get(key), defaultValue); + } + + /** + * Converts a value to boolean - this will be true only if the value is either the empty string, + * or any Object which has a toString which is case-insensitive equal to "true" + */ + protected final boolean asBoolean(Object value, boolean defaultValue) { + if (value == null) return defaultValue; + + String s = value.toString(); + int sz = s.length(); + switch (sz) { + case 0: + return true; + case 4: + return ((s.charAt(0) | 0x20) == 't') && + ((s.charAt(1) | 0x20) == 'r') && + ((s.charAt(2) | 0x20) == 'u') && + ((s.charAt(3) | 0x20) == 'e'); + } + return false; + } + + /** + * Returns this property as a string + * + * @return this property as a string, or null if the property is null + */ + public final String getString(CompoundName key) { + return getString(key, null); + } + + /** + * Returns this property as a string + * + * @return this property as a string, or null if the property is null + */ + public final String getString(String key) { + return getString(key, null); + } + + /** + * Returns this property as a string + * + * @param key the property key + * @param defaultValue the value to return if this property is null + * @return this property as a string + */ + public final String getString(CompoundName key, String defaultValue) { + return asString(get(key), defaultValue); + } + + /** + * Returns this property as a string + * + * @param key the property key + * @param defaultValue the value to return if this property is null + * @return this property as a string + */ + public final String getString(String key, String defaultValue) { + return asString(get(key), defaultValue); + } + + protected final String asString(Object value, String defaultValue) { + if (value == null) return defaultValue; + return value.toString(); + } + + /** + * Returns a property as an Integer + * + * @return the integer value of the name, or null if the property is null + * @throws NumberFormatException if the given parameter exists but + * have a toString which is not parseable as a number + */ + public final Integer getInteger(CompoundName name) { + return getInteger(name, null); + } + + /** + * Returns a property as an Integer + * + * @return the integer value of the name, or null if the property is null + * @throws NumberFormatException if the given parameter exists but + * have a toString which is not parseable as a number + */ + public final Integer getInteger(String name) { + return getInteger(name, null); + } + + /** + * Returns a property as an Integer + * + * @param name the property name + * @param defaultValue the value to return if this property is null + * @return the integer value for the name + * @throws NumberFormatException if the given parameter does not exist + * or does not have a toString parseable as a number + */ + public final Integer getInteger(CompoundName name, Integer defaultValue) { + return asInteger(get(name), defaultValue); + } + + /** + * Returns a property as an Integer + * + * @param name the property name + * @param defaultValue the value to return if this property is null + * @return the integer value for the name + * @throws NumberFormatException if the given parameter does not exist + * or does not have a toString parseable as a number + */ + public final Integer getInteger(String name, Integer defaultValue) { + return asInteger(get(name), defaultValue); + } + + protected final Integer asInteger(Object value, Integer defaultValue) { + try { + if (value == null) + return defaultValue; + + if (value instanceof Number) + return ((Number)value).intValue(); + + String stringValue = value.toString(); + if (stringValue.isEmpty()) + return defaultValue; + + return Integer.valueOf(stringValue); + } catch (IllegalArgumentException e) { + throw new NumberFormatException("'" + value + "' is not a valid integer"); + } + } + + /** + * Returns a property as a Long + * + * @return the long value of the name, or null if the property is null + * @throws NumberFormatException if the given parameter exists but have a value which + * is not parseable as a number + */ + public final Long getLong(CompoundName name) { + return getLong(name, null); + } + + /** + * Returns a property as a Long + * + * @return the long value of the name, or null if the property is null + * @throws NumberFormatException if the given parameter exists but have a value which + * is not parseable as a number + */ + public final Long getLong(String name) { + return getLong(name, null); + } + + /** + * Returns a property as a Long + * + * @param name the property name + * @param defaultValue the value to return if this property is null + * @return the integer value for this name + * @throws NumberFormatException if the given parameter exists but have a value which + * is not parseable as a number + */ + public final Long getLong(CompoundName name, Long defaultValue) { + return asLong(get(name), defaultValue); + } + + /** + * Returns a property as a Long + * + * @param name the property name + * @param defaultValue the value to return if this property is null + * @return the integer value for this name + * @throws NumberFormatException if the given parameter exists but have a value which + * is not parseable as a number + */ + public final Long getLong(String name, Long defaultValue) { + return asLong(get(name), defaultValue); + } + + protected final Long asLong(Object value, Long defaultValue) { + try { + if (value == null) + return defaultValue; + + if (value instanceof Long) + return (Long) value; + + String stringValue = value.toString(); + if (stringValue.isEmpty()) + return defaultValue; + + return Long.valueOf(value.toString()); + } catch (IllegalArgumentException e) { + throw new NumberFormatException("Not a valid long"); + } + } + + /** + * Returns a property as a Double + * + * @return the double value of the name, or null if the property is null + * @throws NumberFormatException if the given parameter exists but have a value which + * is not parseable as a number + */ + public final Double getDouble(CompoundName name) { + return getDouble(name, null); + } + + /** + * Returns a property as a Double + * + * @return the double value of the name, or null if the property is null + * @throws NumberFormatException if the given parameter exists but have a value which + * is not parseable as a number + */ + public final Double getDouble(String name) { + return getDouble(name, null); + } + + /** + * Returns a property as a Double + * + * @param name the property name + * @param defaultValue the value to return if this property is null + * @return the integer value for this name + * @throws NumberFormatException if the given parameter exists but have a value which + * is not parseable as a number + */ + public final Double getDouble(CompoundName name, Double defaultValue) { + return asDouble(get(name), defaultValue); + } + + /** + * Returns a property as a Double + * + * @param name the property name + * @param defaultValue the value to return if this property is null + * @return the integer value for this name + * @throws NumberFormatException if the given parameter exists but have a value which + * is not parseable as a number + */ + public final Double getDouble(String name, Double defaultValue) { + return asDouble(get(name), defaultValue); + } + + protected final Double asDouble(Object value, Double defaultValue) { + try { + if (value == null) + return defaultValue; + + if (value instanceof Double) + return (Double) value; + + String stringValue = value.toString(); + if (stringValue.isEmpty()) + return defaultValue; + + return Double.valueOf(value.toString()); + } catch (IllegalArgumentException e) { + throw new NumberFormatException("Not a valid double"); + } + } + + /** + * Clones this instance and recursively all chained instance. + * Implementations should call this and clone their own state as appropriate + */ + public Properties clone() { + try { + Properties clone = (Properties) super.clone(); + if (chained != null) + clone.chained = this.chained.clone(); + return clone; + } catch (CloneNotSupportedException e) { + throw new RuntimeException("Will never happen"); + } + } + + /** Clones a map by deep cloning each value which is cloneable and shallow copying all other values. */ + public static Map<CompoundName, Object> cloneMap(Map<CompoundName, Object> map) { + return cloneHelper.cloneMap(map); + } + + /** Clones this object if it is clonable, and the clone is public. Returns null if not */ + public static Object clone(Object object) { + return cloneHelper.clone(object); + } + +} |