All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.processing.request.Properties Maven / Gradle / Ivy

There is a newer version: 8.499.20
Show newest version
// Copyright Yahoo. 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 getInstance(Class 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 listProperties() {
        return listProperties(CompoundName.empty);
    }

    /**
     * Returns a snapshot of all properties of this - same as listProperties("",context)
     */
    public final Map listProperties(Map context) {
        return listProperties(CompoundName.empty, context, this);
    }

    /**
     * Returns a snapshot of all properties by calling listProperties(path,null)
     */
    public final Map listProperties(CompoundName path) {
        return listProperties(path, null, this);
    }

    /**
     * Returns a snapshot of all properties by calling listProperties(path,null)
     */
    public final Map listProperties(String path) {
        return listProperties(new CompoundName(path), null, this);
    }

    /**
     * Returns a snapshot of all properties by calling listProperties(path,null)
     */
    public final Map listProperties(CompoundName path, Map context) {
        return listProperties(path, context, this);
    }

    /**
     * Returns a snapshot of all properties by calling listProperties(path,null)
     */
    public final Map listProperties(String path, Map context) {
        return listProperties(new CompoundName(path), context, this);
    }

    /**
     * Returns a snapshot of all properties of this having a given path prefix
     * 

* 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 listProperties(CompoundName path, Map 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 *

* 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 listProperties(String path, Map 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 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 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 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 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. *

* 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. *

* 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. *

* 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 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. *

* 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 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.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 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 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.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 cloneMap(Map 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); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy