org.cesecore.util.ui.DynamicUiProperty.java Source code

Java tutorial

Introduction

Here is the source code for org.cesecore.util.ui.DynamicUiProperty.java

Source

/*************************************************************************
 *                                                                       *
 *  CESeCore: CE Security Core                                           *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/
package org.cesecore.util.ui;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.lang.StringUtils;
import org.cesecore.util.Base64;

/**
 * Allows creation of dynamic properties for display in the UI. 
 * 
 * @version $Id$
 */
public class DynamicUiProperty<T extends Serializable> implements Serializable, Cloneable {

    private static final long serialVersionUID = 1L;

    private String name;
    private T defaultValue;
    private List<T> values = new ArrayList<>();
    private Collection<T> possibleValues;
    private DynamicUiPropertyCallback propertyCallback = DynamicUiPropertyCallback.NONE;
    private Class<? extends Serializable> type;

    /**
     * Denotes whether this property can have multiple values. 
     */
    private boolean hasMultipleValues = false;

    /** Constructor required by Serializable */
    public DynamicUiProperty() {
    }

    /**
     * Constructor. Note the T must implement toString().
     * 
     * @param name The name of this property, for display in the UI
     * @param defaultValue the default value, if any.
     * @param possibleValues a Collection of possible values. If set to null no validation will be performed, if set to an empty list then values 
     *        are presumed to be set at runtime. 
     */
    public DynamicUiProperty(final String name, final T defaultValue, final Collection<T> possibleValues) {
        this.name = name;
        this.defaultValue = defaultValue;
        this.values.add(defaultValue);
        this.possibleValues = possibleValues;
        if (defaultValue != null) {
            this.type = defaultValue.getClass();
        }
    }

    public DynamicUiProperty(final String name, final T defaultValue) {
        this.name = name;
        this.defaultValue = defaultValue;
        this.values.add(defaultValue);
        this.possibleValues = null;
        if (defaultValue != null) {
            this.type = defaultValue.getClass();
        }
    }

    /**
     * Copy constructor for DynamicUiProperty objects
     * @param original the original property
     */
    @SuppressWarnings("unchecked")
    public DynamicUiProperty(final DynamicUiProperty<T> original) {
        this.name = original.getName();
        this.defaultValue = original.getDefaultValue();
        this.setHasMultipleValues(original.getHasMultipleValues());
        if (!original.hasMultipleValues) {
            setValue((T) SerializationUtils.clone(original.getValue()));
        } else {
            List<T> clonedValues = new ArrayList<>();
            for (T value : original.getValues()) {
                clonedValues.add((T) SerializationUtils.clone(value));
            }
            setValues(clonedValues);
        }
        this.possibleValues = original.getPossibleValues();
        this.propertyCallback = original.getPropertyCallback();
        this.type = original.getType();
    }

    /**
     * Returns a value of type T from a string. Limited to the basic java types {@link Integer}, {@link String}, {@link Boolean}, {@link Float},
     * {@link Long}
     * 
     * @param value the value to translate
     * @return and Object instantiated as T, or null if value was not of a usable class or was invalid for T
     */
    public Serializable valueOf(String value) {
        if (defaultValue instanceof MultiLineString) {
            return new MultiLineString(value);
        } else if (defaultValue instanceof String) {
            return value;
        } else if (defaultValue instanceof Integer) {
            try {
                return Integer.valueOf(value);
            } catch (NumberFormatException e) {
                return null;
            }
        } else if (defaultValue instanceof Long) {
            try {
                return Long.valueOf(value);
            } catch (NumberFormatException e) {
                return null;
            }
        } else if (defaultValue instanceof Boolean) {
            if (value.equals(Boolean.TRUE.toString()) || value.equals(Boolean.FALSE.toString())) {
                return Boolean.valueOf(value);
            }
        } else if (defaultValue instanceof Float) {
            try {
                return Float.valueOf(value);
            } catch (NumberFormatException e) {
                return null;
            }
        }
        return null;
    }

    /**
     * 
     * @return string representation of the value (for example the string '1' for the Int value 1. Value is retrieved inside as getValue().
     */
    public String getValueAsString() {
        Serializable value = getValue();
        String ret = "";
        if (value instanceof MultiLineString) {
            ret = ((MultiLineString) value).getValue();
        } else if (value instanceof String) {
            ret = (String) value;
        } else if (value instanceof Integer) {
            try {
                ret = ((Integer) value).toString();
            } catch (NumberFormatException e) {
            }
        } else if (value instanceof Long) {
            try {
                ret = ((Long) value).toString();
            } catch (NumberFormatException e) {
            }
        } else if (value instanceof Boolean) {
            ret = ((Boolean) value).toString();
        } else if (value instanceof Float) {
            try {
                ret = ((Float) value).toString();
            } catch (NumberFormatException e) {
            }
        } else if (value instanceof RadioButton) {
            ret = ((RadioButton) value).getLabel();
        }
        return ret;
    }

    public String getName() {
        return name;
    }

    /**
     * 
     * @return the type class of this property, based on the default value. If the default was null, then the type has to be set explicitly.
     */
    public Class<? extends Serializable> getType() {
        return type;
    }

    public void setType(final Class<? extends Serializable> type) {
        this.type = type;
    }

    public T getDefaultValue() {
        return defaultValue;
    }

    public void setDefaultValue(T defaultValue) {
        this.defaultValue = defaultValue;
    }

    public List<T> getValues() {
        if (!hasMultipleValues) {
            throw new IllegalStateException(
                    "Attempted to draw multiple values from a dynamic property with a single value.");
        }
        return values;
    }

    public T getValue() {
        if (hasMultipleValues) {
            throw new IllegalStateException(
                    "Attempted to draw single value from a dynamic property with multiple value.");
        }
        return values.get(0);
    }

    public List<String> getPossibleValuesAsStrings() {
        List<String> pvs = new ArrayList<String>();

        if (StringUtils.equals(type.getSimpleName(), RadioButton.class.getSimpleName())) {
            for (T pv : getPossibleValues()) {
                RadioButton rb = (RadioButton) pv;
                pvs.add(rb.getLabel());
            }
        } else {
            for (T pv : getPossibleValues()) {
                pvs.add(pv.toString());
            }
        }
        return pvs;
    }

    public Collection<T> getPossibleValues() {
        return possibleValues;
    }

    @SuppressWarnings("unchecked")
    public void setPossibleValues(Collection<? extends Serializable> collection) {
        this.possibleValues = (Collection<T>) collection;
    }

    public boolean isMultiValued() {
        return possibleValues != null;
    }

    public void setValue(T object) {
        if (hasMultipleValues) {
            throw new IllegalStateException(
                    "Attempted to set multiple values from a dynamic property with single value.");
        }
        if (object == null) {
            this.values = new ArrayList<>(Arrays.asList(defaultValue));
        } else {
            if (possibleValues != null) {
                for (final T possibleValue : possibleValues) {
                    if (possibleValue.equals(object)) {
                        this.values = new ArrayList<>(Arrays.asList(object));
                        return;
                    }
                }
                throw new IllegalArgumentException(object + " is not in the list of approved objects.");
            } else {
                this.values = new ArrayList<>(Arrays.asList(object));
            }
        }
    }

    public void setValues(List<T> objects) {
        if (!hasMultipleValues) {
            throw new IllegalStateException(
                    "Attempted to set single value from a dynamic property with multiple values.");
        }
        if (objects == null || objects.isEmpty()) {
            this.values.set(0, defaultValue);
        } else {
            if (possibleValues != null && !possibleValues.isEmpty()) {
                final List<T> values = new ArrayList<>();
                OBJECT_LOOP: for (final T object : objects) {
                    for (final T possibleValue : possibleValues) {
                        if (possibleValue.equals(object)) {
                            values.add(object);
                            continue OBJECT_LOOP;
                        }
                    }
                    throw new IllegalArgumentException(object + " is not in the list of approved objects.");
                }
                this.values = values;
            } else {
                this.values = objects;
            }
        }
    }

    public String getEncodedValue() {
        return getAsEncodedValue(getValue());
    }

    public List<String> getEncodedValues() {
        return getAsEncodedValues(getValues());
    }

    public String getAsEncodedValue(final Serializable possibleValue) {
        return new String(Base64.encode(getAsByteArray(possibleValue), false));
    }

    private List<String> getAsEncodedValues(final List<T> list) {
        final List<String> encodedValues = new ArrayList<>();
        for (final Serializable possibleValue : list) {
            encodedValues.add(new String(Base64.encode(getAsByteArray(possibleValue), false)));
        }
        return encodedValues;
    }

    @SuppressWarnings("unchecked")
    public void setEncodedValue(String encodedValue) {
        setValue((T) getAsObject(Base64.decode(encodedValue.getBytes())));
    }

    @SuppressWarnings("unchecked")
    public void setEncodedValues(List<String> encodedValues) {
        List<T> decodedValues = new ArrayList<>();
        for (String encodedValue : encodedValues) {
            decodedValues.add((T) getAsObject(Base64.decode(encodedValue.getBytes())));
        }
        setValues(decodedValues);
    }

    @SuppressWarnings("unchecked")
    public void setValueGeneric(Serializable object) {
        if (object == null) {
            this.values = new ArrayList<>(Arrays.asList(defaultValue));
        } else {
            this.values = new ArrayList<>(Arrays.asList((T) object));
        }
    }

    @SuppressWarnings("unchecked")
    public void setValuesGeneric(List<? extends Serializable> list) {
        if (list == null || list.isEmpty()) {
            this.values = new ArrayList<>(Arrays.asList(defaultValue));
        } else {
            final List<T> values = new ArrayList<>();
            for (final Serializable object : list) {
                values.add((T) object);
            }
            this.values = values;
        }
    }

    /** @return deep cloned version of this object */
    @SuppressWarnings("unchecked")
    @Override
    public DynamicUiProperty<T> clone() {
        return (DynamicUiProperty<T>) getAsObject(getAsByteArray(this));
    }

    private byte[] getAsByteArray(final Serializable o) {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (final ObjectOutputStream oos = new ObjectOutputStream(baos);) {
            oos.writeObject(o);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        return baos.toByteArray();
    }

    public static Serializable getAsObject(String encodedValue) {
        return getAsObject(Base64.decode(encodedValue.getBytes()));
    }

    private static Serializable getAsObject(final byte[] bytes) {
        try (final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));) {
            return (Serializable) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }
    }

    public DynamicUiPropertyCallback getPropertyCallback() {
        return propertyCallback;
    }

    public void setPropertyCallback(DynamicUiPropertyCallback propertyCallback) {
        this.propertyCallback = propertyCallback;
    }

    /**
     * @return true if this property can have multiple values. 
     */
    public boolean getHasMultipleValues() {
        return hasMultipleValues;
    }

    public void setHasMultipleValues(boolean hasMultipleValues) {
        this.hasMultipleValues = hasMultipleValues;
    }

    /** Returns the current value, like getValue, but has a workaround for JSF bug with ui:repeat and rendered. See ECA-5342 */
    @SuppressWarnings("unchecked")
    public T getJsfBooleanValue() {
        if (hasMultipleValues || type != Boolean.class) {
            // In this case, JSF made a spurious call and will throw away the return value, but it must be of expected type (boolean)
            return (T) Boolean.FALSE;
        } else {
            return getValue();
        }
    }

    /** Sets the value, by calling setValue. Needed for the getJsfBooleanValue workaround */
    public void setJsfBooleanValue(final T newValue) {
        setValue(newValue);
    }
}