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

org.apache.openjpa.lib.conf.Value Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openjpa.lib.conf;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.ParseException;

/**
 * A configuration value.
 *
 * @author Marc Prud'hommeaux
 * @author Pinaki Poddar
 */
public abstract class Value implements Cloneable {

    private static final String[] EMPTY_ALIASES = new String[0];
    private static final Localizer s_loc = Localizer.forPackage(Value.class);
    public static final String INVISIBLE = "******";

    private String prop = null;
    private String loadKey = null;
    private String def = null;
    private String[] aliases = null;
    private String getter = null;
    private List listeners = null;
    private boolean aliasListComprehensive = false;
    private Class scope = null;
    private boolean isDynamic = false;
    private String originalValue = null;
    private Set otherNames = null;
    private boolean _hidden  = false;
    private boolean _private = false;

    /**
     * Default constructor.
     */
    public Value() {
    }

    /**
     * Constructor. Supply the property name.
     *
     * @see #setProperty
     */
    public Value(String prop) {
        setProperty(prop);
    }

    /**
     * The property name that will be used when setting or
     * getting this value in a {@link Map}.
     */
    public String getProperty() {
        return prop;
    }

    /**
     * The property name that will be used when setting or
     * getting this value in a {@link Map}.
     */
    public void setProperty(String prop) {
        this.prop = prop;
    }

    /**
     * Adds a moniker that is equivalent to the original property key used
     * during construction.
     *
     * @since 2.0.0
     */
    public void addEquivalentKey(String other) {
        if (otherNames == null)
            otherNames = new HashSet<>();
        otherNames.add(other);
    }

    /**
     * Gets the unmodifiable view of the equivalent keys or an empty set if
     * no equivalent key has been added.
     *
     * @since 2.0.0
     */
    public Set getEquivalentKeys() {
        return otherNames == null ? Collections.EMPTY_SET
            : Collections.unmodifiableSet(otherNames);
    }

    /**
     * Gets unmodifiable view of all the property keys set on this receiver.
     * The 0-th element in the returned list is always the same as the original
     * key returned by {@link #getProperty()} method.
     *
     * @since 2.0.0
     */
    public List getPropertyKeys() {
        List result = new ArrayList<>(1 +
            (otherNames ==null ? 0 : otherNames.size()));
        result.add(getProperty());
        if (otherNames != null)
            result.addAll(otherNames);
        return Collections.unmodifiableList(result);
    }

    /**
     * Affirms if the given key matches the property (or any of its equivalent).
     *
     * @since 2.0.0
     */
    public boolean matches(String p) {
        return getProperty().equals(p) ||
          (otherNames != null && otherNames.contains(p));
    }

    /**
     * The key under which this value was loaded, or null.
     */
    public String getLoadKey() {
        return loadKey;
    }

    /**
     * Sets key under which this value was loaded.
     * @exception if called with a non-null key which is different from an
     * already loaded key.
     */
    public void setLoadKey(String key) {
        if (this.loadKey != null && key != null && !this.loadKey.equals(key))
            throw new ParseException(s_loc.get("multiple-load-key",
                loadKey, key));
        loadKey = key;
    }

    /**
     * Aliases for the value in the form key1, value1, key2, value2, ...
     * All alias values must be in string form.
     */
    public String[] getAliases() {
        return (aliases == null) ? EMPTY_ALIASES : aliases;
    }

    /**
     * Aliases for the value in the form key1, value1, key2, value2, ...
     * All alias values must be in string form.
     * 

* To avoid potential side-effects, this method copies the array passed in. */ public void setAliases(String[] aliases) { String [] aStrings = new String[aliases.length]; System.arraycopy(aliases, 0, aStrings, 0, aStrings.length); this.aliases = aStrings; } /** * Replaces an existing alias, or adds the given alias to the front of the * alias list if it does not already exist. All alias values must be in * string form. */ public void setAlias(String key, String value) { aliases = setAlias(key, value, aliases); } /** * Set an alias into a current alias list, returning the new list. */ protected String[] setAlias(String key, String value, String[] aliases) { if (aliases == null) aliases = EMPTY_ALIASES; for (int i = 0; i < aliases.length; i += 2) { if (key.equals(aliases[i])) { aliases[i + 1] = value; return aliases; } } // add as new alias String[] newAliases = new String[aliases.length + 2]; System.arraycopy(aliases, 0, newAliases, 2, aliases.length); newAliases[0] = key; newAliases[1] = value; return newAliases; } /** * Whether or not the alias list defines all possible settings for this * value. If so, an error will be generated when attempting to invoke * any method on this value with an unknown option. */ public boolean isAliasListComprehensive() { return aliasListComprehensive; } /** * Whether or not the alias list defines all possible settings for this * value. If so, an error will be generated when attempting to invoke * any method on this value with an unknown option. */ public void setAliasListComprehensive(boolean aliasListIsComprehensive) { this.aliasListComprehensive = aliasListIsComprehensive; } /** * Alias the given setting. */ public String alias(String str) { return alias(str, aliases, false); } /** * Alias the given setting. */ protected String alias(String str, String[] aliases, boolean nullNotFound) { if (str != null) str = str.trim(); if (aliases == null || aliases.length == 0) return (nullNotFound) ? null : str; boolean empty = str != null && str.length() == 0; for (int i = 1; i < aliases.length; i += 2) if (Objects.equals(str, aliases[i]) || (empty && aliases[i] == null)) return aliases[i - 1]; return (nullNotFound) ? null : str; } /** * Unalias the given setting. */ public String unalias(String str) { return unalias(str, aliases, false); } /** * Unalias the given setting. */ protected String unalias(String str, String[] aliases, boolean nullNotFound) { if (str != null) str = str.trim(); boolean empty = str != null && str.length() == 0; if (str == null || (empty && def != null)) str = def; if (aliases != null) for (int i = 0; i < aliases.length; i += 2) if (Objects.equals(str, aliases[i]) || Objects.equals(str, aliases[i + 1]) || (empty && aliases[i] == null)) return aliases[i + 1]; if (isAliasListComprehensive() && aliases != null) throw new ParseException(s_loc.get("invalid-enumerated-config", getProperty(), str, Arrays.asList(aliases))); return (nullNotFound) ? null : str; } /** * The default value for the property as a string. */ public String getDefault() { return def; } /** * The default value for the property as a string. */ public void setDefault(String def) { this.def = def; } /** * The name of the getter method for the instantiated value of this * property(as opposed to the string value) */ public String getInstantiatingGetter() { return getter; } /** * The name of the getter method for the instantiated value of this * property(as opposed to the string value). If the string starts with * this., then the getter will be looked up on the value * instance itself. Otherwise, the getter will be looked up on the * configuration instance. */ public void setInstantiatingGetter(String getter) { this.getter = getter; } /** * A class defining the scope in which this value is defined. This will * be used by the configuration framework to look up metadata about * the value. */ public Class getScope() { return scope; } /** * A class defining the scope in which this value is defined. This will * be used by the configuration framework to look up metadata about * the value. */ public void setScope(Class cls) { scope = cls; } /** * Return a stringified version of this value. If the current value has * a short alias key, the alias key is returned. */ public String getString() { return alias(getInternalString()); } /** * Set this value from the given string. If the given string is null or * empty and a default is defined, the default is used. If the given * string(or default) is an alias key, it will be converted to the * corresponding value internally. *
* If this Value is being set to a non-default value for the first time * (as designated by originalString being null), then the * value is remembered as original. This original value is used * for equality and hashCode computation if this Value is * {@link #isDynamic() dynamic}. * */ public void setString(String val) { assertChangeable(); String str = unalias(val); try { setInternalString(str); if (originalValue == null && val != null && !isDefault(val)) { originalValue = getString(); } } catch (ParseException pe) { throw pe; } catch (RuntimeException re) { throw new ParseException(prop + ": " + val, re); } } /** * Set this value as an object. *
* If this Value is being set to a non-default value for the first time * (as designated by originalString being null), then the * value is remembered as original. This original value is used * for equality and hashCode computation if this Value is * {@link #isDynamic() dynamic}. * */ public void setObject(Object obj) { // if setting to null set as string to get defaults into play if (obj == null && def != null) setString(null); else { try { setInternalObject(obj); if (originalValue == null && obj != null && !isDefault(obj)) { originalValue = getString(); } } catch (ParseException pe) { throw pe; } catch (RuntimeException re) { throw new ParseException(prop + ": " + obj, re); } } } /** * Gets the original value. Original value denotes the Stringified form of * this Value, from which it has been set, if ever. If this Value has never * been set to a non-default value, then returns the default value, which * itself can be null. * * @since 1.1.0 */ public String getOriginalValue() { return (originalValue == null) ? getDefault() : originalValue; } boolean isDefault(Object val) { return val != null && val.toString().equals(getDefault()); } /** * Returns the type of the property that this Value represents. */ public abstract Class getValueType(); /** * Return the internal string form of this value. */ protected abstract String getInternalString(); /** * Set this value from the given string. */ protected abstract void setInternalString(String str); /** * Set this value from an object. */ protected abstract void setInternalObject(Object obj); /** * Gets unmodifable list of listeners for value changes. */ public List getListeners() { return Collections.unmodifiableList(this.listeners); } /** * Listener for value changes. */ public void addListener(ValueListener listener) { if (listener == null) return; if (listeners == null) listeners = new ArrayList<>(); listeners.add(listener); } public void removeListener(ValueListener listener) { if (listener == null) return; listeners.remove(listener); } /** * Subclasses should call this method when their internal value changes. */ public void valueChanged() { if (listeners == null) return; for (ValueListener listener : listeners) { listener.valueChanged(this); } } /** * Asserts if this receiver can be changed. * Subclasses must invoke this method before changing its * internal state. * * This receiver can not be changed if all of the following is true *

  • this receiver is not dynamic *
  • ValueListener attached to this receiver is a Configuration *
  • Configuration is read-only */ protected void assertChangeable() { if (!isDynamic() && containsReadOnlyConfigurationAsListener()) { throw new RuntimeException(s_loc.get("veto-change", this.getProperty()).toString()); } } boolean containsReadOnlyConfigurationAsListener() { if (listeners == null) return false; for (ValueListener listener : listeners) { if (listener instanceof Configuration && ((Configuration)listener).isReadOnly()) return true; } return false; } /** * Sets if this receiver can be mutated even when the configuration it * belongs to has been {@link Configuration#isReadOnly() frozen}. * * @since 1.1.0 */ public void setDynamic(boolean flag) { isDynamic = flag; } /** * Affirms if this receiver can be mutated even when the configuration it * belongs to has been {@link Configuration#isReadOnly() frozen}. * * @since 1.1.0 */ public boolean isDynamic() { return isDynamic; } /** * Use {@link #getOriginalValue() original value} instead of * {@link #getString() current value} because they are one and the same * for non-dynamic Values and ensures that modifying dynamic Values do not * impact equality or hashCode contract. */ @Override public int hashCode() { String str = (isDynamic()) ? getOriginalValue() : getString(); int strHash = (str == null) ? 0 : str.hashCode(); int propHash = (prop == null) ? 0 : prop.hashCode(); return strHash ^ propHash; } /** * Use {@link #getOriginalValue() original value} instead of * {@link #getString() current value} because they are one and the same * for non-dynamic Values and ensures that modifying dynamic Values do not * impact equality or hashCode contract. */ @Override public boolean equals(Object other) { if (other == this) return true; if (!(other instanceof Value)) return false; Value o = (Value) other; String thisStr = (isDynamic()) ? getOriginalValue() : getString(); String thatStr = (isDynamic()) ? o.getOriginalValue() : o.getString(); return (isDynamic() == o.isDynamic()) && Objects.equals(prop, o.getProperty()) && Objects.equals(thisStr, thatStr); } @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException cnse) { return null; } } /** * Affirms if the value for this Value is visible. * Certain sensitive value such as password can be made invisible * so that it is not returned to the user code. */ public boolean isHidden() { return _hidden; } /** * Hides the value of this Value from being output to the caller. */ public void hide() { _hidden = true; } /** * Affirms if this Value is used for internal purpose only and not exposed as a supported property. * @see Configuration#getPropertyKeys() */ public boolean isPrivate() { return _private; } /** * Marks this Value for internal purpose only. */ public void makePrivate() { _private = true; } /** * Get the actual data stored in this value. */ public abstract Object get(); @Override public String toString() { return getProperty()+ ":" + get() + "[" + getValueType().getName() + "]"; } }




  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy