001package jmri.beans;
002
003import java.util.Arrays;
004import java.util.HashMap;
005import java.util.HashSet;
006import java.util.Set;
007
008/**
009 * Provide support for converging the Arbitrary* classes with the non-arbitrary
010 * versions of those classes so that the Arbitrary* version can extend the
011 * non-arbitrary class.
012 * 
013 * @author Randall Wood Copyright 2015, 2020
014 */
015public class ArbitraryPropertySupport implements BeanInterface {
016
017    private final HashMap<String, Object> properties = new HashMap<>();
018    private final UnboundBean bean;
019
020    public ArbitraryPropertySupport(UnboundBean bean) {
021        this.bean = bean;
022    }
023
024    /** {@inheritDoc} */
025    @Override
026    public void setIndexedProperty(String key, int index, Object value) {
027        if (BeanUtil.hasIntrospectedProperty(this.bean, key)) {
028            BeanUtil.setIntrospectedIndexedProperty(this.bean, key, index, value);
029        } else {
030            if (!this.properties.containsKey(key)) {
031                this.properties.put(key, new Object[1]);
032            }
033            Object[] array = (Object[]) this.properties.get(key);
034            if (index < array.length) {
035                array[index] = value;
036            } else {
037                Object[] grown = Arrays.copyOf(array, index + 1);
038                grown[index] = value;
039                this.properties.put(key, grown);
040            }
041        }
042    }
043
044    /** {@inheritDoc} */
045    @Override
046    public Object getIndexedProperty(String key, int index) {
047        if (this.properties.containsKey(key) && this.properties.get(key).getClass().isArray()) {
048            try {
049                return ((Object[]) this.properties.get(key))[index];
050            } catch (ArrayIndexOutOfBoundsException ex) {
051                return null;
052            }
053        }
054        return BeanUtil.getIntrospectedIndexedProperty(this.bean, key, index);
055    }
056
057    /** {@inheritDoc} */
058    @Override
059    public void setProperty(String key, Object value) {
060        // use write method for property if it exists
061        if (BeanUtil.hasIntrospectedProperty(this.bean, key)) {
062            BeanUtil.setIntrospectedProperty(this.bean, key, value);
063        } else {
064            this.properties.put(key, value);
065        }
066    }
067
068    /** {@inheritDoc} */
069    @Override
070    public Object getProperty(String key) {
071        if (this.properties.containsKey(key)) {
072            return this.properties.get(key);
073        }
074        return BeanUtil.getIntrospectedProperty(this.bean, key);
075    }
076
077    /** {@inheritDoc} */
078    @Override
079    public boolean hasProperty(String key) {
080        return (this.properties.containsKey(key) || BeanUtil.hasIntrospectedProperty(this.bean, key));
081    }
082
083    /** {@inheritDoc} */
084    @Override
085    public boolean hasIndexedProperty(String key) {
086        return ((this.properties.containsKey(key) && this.properties.get(key).getClass().isArray()) ||
087                BeanUtil.hasIntrospectedIndexedProperty(this.bean, key));
088    }
089
090    /** {@inheritDoc} */
091    @Override
092    public Set<String> getPropertyNames() {
093        HashSet<String> names = new HashSet<>();
094        names.addAll(this.properties.keySet());
095        names.addAll(BeanUtil.getIntrospectedPropertyNames(this.bean));
096        return names;
097    }
098
099}