eu.eidas.auth.commons.PersonalAttribute.java Source code

Java tutorial

Introduction

Here is the source code for eu.eidas.auth.commons.PersonalAttribute.java

Source

/*
 * This work is Open Source and licensed by the European Commission under the
 * conditions of the European Public License v1.1
 *
 * (http://www.osor.eu/eupl/european-union-public-licence-eupl-v.1.1);
 *
 * any use of this file implies acceptance of the conditions of this license.
 * 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 eu.eidas.auth.commons;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import eu.eidas.auth.commons.lang.Canonicalizers;
import eu.eidas.auth.commons.lang.EnumMapper;
import eu.eidas.auth.commons.lang.KeyAccessor;
import eu.eidas.util.Preconditions;

/**
 * Stores all the information relative to the PersonalAttribute.
 * <p/>
 * Note that every iterator obtained from this class MUST synchronize around its iteration loop.
 *
 * @deprecated use {@link eu.eidas.auth.model.attribute.ImmutablePersonalAttribute} instead.
 */
@Deprecated
@ThreadSafe
public final class PersonalAttribute {

    public enum Status {
        /**
         * Attribute is Available.
         */
        AVAILABLE("Available"),

        /**
         * Attribute is NotAvailable.
         */
        NOT_AVAILABLE("NotAvailable");

        private static final EnumMapper<String, Status> MAPPER = new EnumMapper<String, Status>(
                new KeyAccessor<String, Status>() {

                    @Nonnull
                    @Override
                    public String getKey(@Nonnull Status stat) {
                        return stat.getValue();
                    }
                }, Canonicalizers.trimLowerCase(), values());

        @Nullable
        public static Status fromString(@Nonnull String val) {
            return MAPPER.fromKey(val);
        }

        public static EnumMapper<String, Status> mapper() {
            return MAPPER;
        }

        @Nonnull
        private final transient String value;

        Status(@Nonnull String value) {
            this.value = value;
        }

        @Nonnull
        public String getValue() {
            return value;
        }

        @Nonnull
        @Override
        public String toString() {
            return value;
        }
    }

    /**
     * Guesses the friendly name from the given full attribute name URI.
     *
     * @param name the attribute name URI
     * @return the guessed friendly name
     */
    public static String extractFriendlyName(@Nonnull String name) {
        Preconditions.checkNotNull(name, "name");
        if (StringUtils.isBlank(name)) {
            return StringUtils.EMPTY;
        }
        int lastIndexOf = name.lastIndexOf('/');
        if (lastIndexOf == -1 || lastIndexOf == name.length() - 1) {
            return name;
        }
        return name.substring(lastIndexOf + 1);
    }

    /**
     * Logger object.
     */
    private static final Logger LOG = LoggerFactory.getLogger(PersonalAttribute.class.getName());

    /**
     * Name of the personal attribute as a URI.
     * <p/>
     * This is the full name as in {@code http://eidas.europa.eu/attributes/naturalperson/CurrentFamilyName}.
     */
    private final String name;

    /**
     * Friendly name of this personal attribute.
     * <p/>
     * For example for {@code http://eidas.europa.eu/attributes/naturalperson/CurrentGivenName}, the friendly name is
     * {@code FirstName}.
     */
    private final String friendlyName;

    /**
     * Values of the personal attribute.
     */
    @Nonnull
    private List<String> value = Collections.synchronizedList(new ArrayList<String>());

    /**
     * Complex values of the personal attribute.
     */
    @Nonnull
    private Map<String, String> complexValue = Collections.synchronizedMap(new HashMap<String, String>());

    /**
     * Is the personal attribute mandatory?
     */
    private boolean required;

    /**
     * set to true when the attribute is an eIDAS natural person attribute
     */
    private boolean eidasNaturalPersonAttr;

    /**
     * set to true when the attribute is an eIDAS legal person attribute
     */
    private boolean eidasLegalPersonAttr;

    /**
     * Copy constructor which can change the name and copies all values.
     *
     * @param copy the instance to copy
     */
    public PersonalAttribute(@Nonnull PersonalAttribute copy, @Nonnull String newName,
            @Nonnull String newFriendlyName) {
        this(copy, newName, newFriendlyName, true);
    }

    /**
     * Copy constructor which keeps the same name and copies all values.
     *
     * @param copy the instance to copy
     */
    public PersonalAttribute(@Nonnull PersonalAttribute copy) {
        this(copy, copy.getName(), copy.getFriendlyName(), true);
    }

    /**
     * Copy constructor
     *
     * @param copy the instance to copy
     * @param copyAllValues {@code true} if all the values must also be copied.
     */
    private PersonalAttribute(@Nonnull PersonalAttribute copy, @Nonnull String newName,
            @Nonnull String newFriendlyName, boolean copyAllValues) {
        Preconditions.checkNotNull(copy, "copy");
        Preconditions.checkNotNull(newName, "newName");
        Preconditions.checkNotNull(newFriendlyName, "newFriendlyName");
        name = newName;
        friendlyName = newFriendlyName;
        //noinspection SynchronizationOnLocalVariableOrMethodParameter
        synchronized (copy) {
            // lock obtention optimization // no deadlock possibility since this instance is not created yet
            //noinspection NestedSynchronizedStatement
            synchronized (this) {
                if (copyAllValues) {
                    setValue(copy.getValue());
                    setComplexValue(copy.getComplexValue());
                }
                setIsRequired(copy.isRequired());
                setEidasNaturalPersonAttr(copy.isEidasNaturalPersonAttr());
                setEidasLegalPersonAttr(copy.isEidasLegalPersonAttr());
            }
        }
    }

    /**
     * Default Constructor.
     */
    public PersonalAttribute(@Nonnull String name, @Nonnull String friendlyName) {
        Preconditions.checkNotNull(name, "name");
        if (StringUtils.isBlank(friendlyName)) {
            friendlyName = extractFriendlyName(name);
        }
        this.name = name;
        this.friendlyName = friendlyName;
    }

    public PersonalAttribute(@Nonnull String name, @Nonnull String friendlyName, boolean isRequired) {
        this(name, friendlyName);
        setIsRequired(isRequired);
    }

    /**
     * PersonalAttribute Constructor for complex values.
     *
     * @param attrName The attribute name.
     * @param attrIsRequired The attribute type value.
     * @param simpleValue The attribute's value.
     */
    public PersonalAttribute(@Nonnull String attrName, @Nonnull String friendlyName, final boolean attrIsRequired,
            @Nonnull List<String> simpleValue) {
        this(attrName, friendlyName);
        // lock obtention optimization
        synchronized (this) {
            this.setIsRequired(attrIsRequired);
            this.setValue(simpleValue);
        }
    }

    /**
     * PersonalAttribute Constructor for complex values.
     *
     * @param attrName The attribute name.
     * @param attrIsRequired The attribute type value.
     * @param attrComplexValue The attribute's complex value.
     */
    @Deprecated
    public PersonalAttribute(@Nonnull String attrName, @Nonnull String friendlyName, final boolean attrIsRequired,
            @Nonnull Map<String, String> attrComplexValue) {
        this(attrName, friendlyName);
        // lock obtention optimization
        synchronized (this) {
            this.setIsRequired(attrIsRequired);
            this.setComplexValue(attrComplexValue);
        }
    }

    /**
     * Static factory to create a new copy of the given instance, including all values.
     *
     * @param copy the instance to copy
     * @return a new copy
     */
    @Nullable
    @SuppressWarnings("unchecked")
    public static PersonalAttribute copyOf(@Nullable PersonalAttribute copy) {
        if (null == copy) {
            return null;
        }
        return new PersonalAttribute(copy, copy.getName(), copy.getFriendlyName(), true);
    }

    /**
     * Static factory to create a new copy of the given instance, excluding all values.
     *
     * @param copy the instance to copy without any value
     * @return a new copy without any value
     */
    @Nullable
    @SuppressWarnings("unchecked")
    public static PersonalAttribute copyOfWithoutValues(@Nullable PersonalAttribute copy) {
        if (null == copy) {
            return null;
        }
        return new PersonalAttribute(copy, copy.getName(), copy.getFriendlyName(), false);
    }

    /**
     * Static factory to create a new copy of the given instance, including all values and which can change the name.
     *
     * @param copy the instance to copy
     * @return a new copy
     */
    @Nullable
    @SuppressWarnings("unchecked")
    public static PersonalAttribute copyOfWithNewName(@Nullable PersonalAttribute copy, @Nonnull String newName,
            @Nonnull String newFriendlyName) {
        if (null == copy) {
            return null;
        }
        return new PersonalAttribute(copy, newName, newFriendlyName, true);
    }

    /**
     * Static factory to create a new copy of the given instance, excluding all values and which can change the name.
     *
     * @param copy the instance to copy without any value
     * @return a new copy without any value
     */
    @Nullable
    @SuppressWarnings("unchecked")
    public static PersonalAttribute copyOfWithNewNameWithoutValues(@Nullable PersonalAttribute copy,
            @Nonnull String newName, @Nonnull String newFriendlyName) {
        if (null == copy) {
            return null;
        }
        return new PersonalAttribute(copy, newName, newFriendlyName, false);
    }

    /**
     * Getter for the required value.
     *
     * @return The required value.
     */
    public synchronized boolean isRequired() {
        return required;
    }

    /**
     * Setter for the required value.
     *
     * @param attrIsRequired this attribute?
     */
    public synchronized void setIsRequired(final boolean attrIsRequired) {
        this.required = attrIsRequired;
    }

    /**
     * Returns the name of this personal attribute as a URI.
     * <p/>
     * This is the full name as in {@code http://eidas.europa.eu/attributes/naturalperson/CurrentFamilyName}.
     *
     * @return The name value.
     */
    public String getName() {
        return name;
    }

    /**
     * /** Friendly Name of this personal attribute.
     * <p/>
     * For example, for {@code http://eidas.europa.eu/attributes/naturalperson/CurrentFamilyName}, the friendly name is
     * {@code CurrentFamilyName}.
     *
     * @return The friendly name value.
     */
    public String getFriendlyName() {
        return friendlyName;
    }

    /**
     * Getter for the value.
     *
     * @return The list of values.
     */
    @Nonnull
    public synchronized List<String> getValue() {
        return value;
    }

    @Nonnull
    public synchronized String getDisplayValue() {
        if (value.size() == 1) {
            return value.get(0);
        } else {
            return value.toString();
        }
    }

    /**
     * Setter for the list of values.
     *
     * @param attrValue The personal attribute value.
     */
    public synchronized void setValue(@Nullable List<String> attrValue) {
        // no defensive copy needed when there is no reference update
        if (value == attrValue) {
            return;
        }
        if (null == attrValue || attrValue.isEmpty()) {
            value.clear();
            return;
        }
        ArrayList<String> defensiveCopy = new ArrayList<String>();
        defensiveCopy.addAll(attrValue);
        value = Collections.synchronizedList(defensiveCopy);
    }

    /**
     * Getter for the status.
     *
     * @return The status.
     */
    public Status getStatus() {
        if (isEmpty()) {
            return Status.NOT_AVAILABLE;
        }
        return Status.AVAILABLE;
    }

    /**
     * Returns {@code true} if this attribute has no simple value and no complex value, {@code false} otherwise.
     *
     * @return {@code true} if this attribute has no simple value and no complex value, {@code false} otherwise.
     */
    public synchronized boolean isEmpty() {
        // atomically check both the list and the map
        return isEmptyValue() && isEmptyComplexValue();
    }

    /**
     * Getter for the complex value.
     *
     * @return The complex value.
     */
    @Nonnull
    public synchronized Map<String, String> getComplexValue() {
        return complexValue;
    }

    /**
     * Setter for the complex value.
     *
     * @param complexVal The personal attribute Complex value.
     */
    public synchronized void setComplexValue(@Nullable Map<String, String> complexVal) {
        // no defensive copy needed when there is no reference update
        if (this.complexValue == complexVal) {
            return;
        }
        if (null == complexVal || complexVal.isEmpty()) {
            this.complexValue.clear();
            return;
        }
        this.complexValue = Collections.synchronizedMap(new HashMap<String, String>(complexVal));
    }

    /**
     * Return true the value is empty.
     *
     * @return True if the value is empty "[]";
     */
    public synchronized boolean isEmptyValue() {
        return value.isEmpty() || (value.size() == 1 && StringUtils.isEmpty(value.get(0)));
    }

    /**
     * Returns true if the Complex Value is empty.
     *
     * @return True if the Complex Value is empty;
     */
    public synchronized boolean isEmptyComplexValue() {
        return complexValue.isEmpty();
    }

    public synchronized boolean isEidasNaturalPersonAttr() {
        return eidasNaturalPersonAttr;
    }

    public synchronized void setEidasNaturalPersonAttr(boolean eidasNaturalPersonAttrArg) {
        this.eidasNaturalPersonAttr = eidasNaturalPersonAttrArg;
    }

    public synchronized boolean isEidasLegalPersonAttr() {
        return eidasLegalPersonAttr;
    }

    public synchronized void setEidasLegalPersonAttr(boolean eidasLegalPersonAttrArg) {
        this.eidasLegalPersonAttr = eidasLegalPersonAttrArg;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        PersonalAttribute that = (PersonalAttribute) o;

        // Open call for final fields:
        if (name != null ? !name.equals(that.name) : that.name != null) {
            return false;
        }
        if (friendlyName != null ? !friendlyName.equals(that.friendlyName) : that.friendlyName != null) {
            return false;
        }

        synchronized (this) {
            // A deadlock is possible here if 2 threads try to equals() each other instances...
            synchronized (that) {
                if (required != that.required) {
                    return false;
                }
                if (eidasNaturalPersonAttr != that.eidasNaturalPersonAttr) {
                    return false;
                }
                if (eidasLegalPersonAttr != that.eidasLegalPersonAttr) {
                    return false;
                }
                if (value != null ? !value.equals(that.value) : that.value != null) {
                    return false;
                }
                return complexValue != null ? complexValue.equals(that.complexValue) : that.complexValue == null;
            }
        }
    }

    @Override
    public synchronized int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (friendlyName != null ? friendlyName.hashCode() : 0);
        result = 31 * result + (value != null ? value.hashCode() : 0);
        result = 31 * result + (complexValue != null ? complexValue.hashCode() : 0);
        result = 31 * result + (required ? 1 : 0);
        result = 31 * result + (eidasNaturalPersonAttr ? 1 : 0);
        result = 31 * result + (eidasLegalPersonAttr ? 1 : 0);
        return result;
    }

    /**
     * Prints the PersonalAttribute in the following format. name:required:[v,a,l,u,e,s]|[v=a,l=u,e=s]:status;
     *
     * @return The PersonalAttribute as a string.
     */
    @Override
    public synchronized String toString() {
        return PersonalAttributeString.toString(this);
    }
}